3 # Electrum - lightweight Bitcoin client
4 # Copyright (C) 2012 thomasv@gitorious
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 import sys, time, datetime, re
21 from util import print_error
26 sys.exit("Error: Could not import PyQt4 on Linux systems, you may try 'sudo apt-get install python-qt4'")
28 from PyQt4.QtGui import *
29 from PyQt4.QtCore import *
30 import PyQt4.QtCore as QtCore
31 import PyQt4.QtGui as QtGui
32 from interface import DEFAULT_SERVERS
37 sys.exit("Error: Could not import icons_rc.py, please generate it with: 'pyrcc4 icons.qrc -o lib/icons_rc.py'")
39 from wallet import format_satoshis
40 from simple_config import SimpleConfig
41 import bmp, mnemonic, pyqrnative, qrscanner
42 from simple_config import SimpleConfig
44 from decimal import Decimal
48 if platform.system() == 'Windows':
49 MONOSPACE_FONT = 'Lucida Console'
50 elif platform.system() == 'Darwin':
51 MONOSPACE_FONT = 'Monaco'
53 MONOSPACE_FONT = 'monospace'
55 ALIAS_REGEXP = '^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$'
57 def numbify(entry, is_int = False):
58 text = unicode(entry.text()).strip()
59 pos = entry.cursorPosition()
61 if not is_int: chars +='.'
62 s = ''.join([i for i in text if i in chars])
67 s = s[:p] + '.' + s[p:p+8]
69 amount = int( Decimal(s) * 100000000 )
78 entry.setCursorPosition(pos)
82 class Timer(QtCore.QThread):
85 self.emit(QtCore.SIGNAL('timersignal'))
88 class HelpButton(QPushButton):
89 def __init__(self, text):
90 QPushButton.__init__(self, '?')
91 self.setFocusPolicy(Qt.NoFocus)
92 self.setFixedWidth(20)
93 self.clicked.connect(lambda: QMessageBox.information(self, 'Help', text, 'OK') )
96 class EnterButton(QPushButton):
97 def __init__(self, text, func):
98 QPushButton.__init__(self, text)
100 self.clicked.connect(func)
102 def keyPressEvent(self, e):
103 if e.key() == QtCore.Qt.Key_Return:
106 class MyTreeWidget(QTreeWidget):
107 def __init__(self, parent):
108 QTreeWidget.__init__(self, parent)
111 for i in range(0,self.viewport().height()/5):
112 if self.itemAt(QPoint(0,i*5)) == item:
116 for j in range(0,30):
117 if self.itemAt(QPoint(0,i*5 + j)) != item:
119 self.emit(SIGNAL('customContextMenuRequested(const QPoint&)'), QPoint(50, i*5 + j - 1))
121 self.connect(self, SIGNAL('itemActivated(QTreeWidgetItem*, int)'), ddfr)
126 class StatusBarButton(QPushButton):
127 def __init__(self, icon, tooltip, func):
128 QPushButton.__init__(self, icon, '')
129 self.setToolTip(tooltip)
131 self.setMaximumWidth(25)
132 self.clicked.connect(func)
135 def keyPressEvent(self, e):
136 if e.key() == QtCore.Qt.Key_Return:
140 class QRCodeWidget(QWidget):
142 def __init__(self, addr):
143 super(QRCodeWidget, self).__init__()
144 self.setGeometry(300, 300, 350, 350)
147 def set_addr(self, addr):
149 self.qr = pyqrnative.QRCode(4, pyqrnative.QRErrorCorrectLevel.L)
150 self.qr.addData(addr)
153 def paintEvent(self, e):
154 qp = QtGui.QPainter()
157 size = self.qr.getModuleCount()*boxsize
158 k = self.qr.getModuleCount()
159 black = QColor(0, 0, 0, 255)
160 white = QColor(255, 255, 255, 255)
163 if self.qr.isDark(r, c):
169 qp.drawRect(c*boxsize, r*boxsize, boxsize, boxsize)
174 def ok_cancel_buttons(dialog):
177 b = QPushButton("OK")
179 b.clicked.connect(dialog.accept)
180 b = QPushButton("Cancel")
182 b.clicked.connect(dialog.reject)
186 class ElectrumWindow(QMainWindow):
188 def __init__(self, wallet):
189 QMainWindow.__init__(self)
191 self.wallet.register_callback(self.update_callback)
193 self.funds_error = False
194 self.completions = QStringListModel()
196 self.tabs = tabs = QTabWidget(self)
197 tabs.addTab(self.create_history_tab(), _('History') )
199 tabs.addTab(self.create_send_tab(), _('Send') )
200 tabs.addTab(self.create_receive_tab(), _('Receive') )
201 tabs.addTab(self.create_contacts_tab(), _('Contacts') )
202 tabs.addTab(self.create_wall_tab(), _('Wall') )
203 tabs.setMinimumSize(600, 400)
204 tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
205 self.setCentralWidget(tabs)
206 self.create_status_bar()
208 g = cfg.config["winpos-qt"]
209 self.setGeometry(g[0], g[1], g[2], g[3])
210 title = 'Electrum ' + self.wallet.electrum_version + ' - ' + self.wallet.path
211 if not self.wallet.seed: title += ' [seedless]'
212 self.setWindowTitle( title )
214 QShortcut(QKeySequence("Ctrl+W"), self, self.close)
215 QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
216 QShortcut(QKeySequence("Ctrl+PgUp"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() - 1 )%tabs.count() ))
217 QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() + 1 )%tabs.count() ))
219 self.connect(self, QtCore.SIGNAL('updatesignal'), self.update_wallet)
220 self.history_list.setFocus(True)
222 # dark magic fix by flatfly; https://bitcointalk.org/index.php?topic=73651.msg959913#msg959913
223 if platform.system() == 'Windows':
224 n = 3 if self.wallet.seed else 2
225 tabs.setCurrentIndex (n)
226 tabs.setCurrentIndex (0)
229 def connect_slots(self, sender):
231 self.connect(sender, QtCore.SIGNAL('timersignal'), self.check_recipient)
232 self.previous_payto_e=''
234 def check_recipient(self):
235 if self.payto_e.hasFocus():
237 r = unicode( self.payto_e.text() )
238 if r != self.previous_payto_e:
239 self.previous_payto_e = r
241 if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
243 to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
247 s = r + ' <' + to_address + '>'
248 self.payto_e.setText(s)
251 def update_callback(self):
252 self.emit(QtCore.SIGNAL('updatesignal'))
254 def update_wallet(self):
255 if self.wallet.interface and self.wallet.interface.is_connected:
256 if self.wallet.blocks == -1:
257 text = _( "Connecting..." )
258 icon = QIcon(":icons/status_disconnected.png")
259 elif self.wallet.blocks == 0:
260 text = _( "Server not ready" )
261 icon = QIcon(":icons/status_disconnected.png")
262 elif not self.wallet.up_to_date:
263 text = _( "Synchronizing..." )
264 icon = QIcon(":icons/status_waiting.png")
266 c, u = self.wallet.get_balance()
267 text = _( "Balance" ) + ": %s "%( format_satoshis(c,False,self.wallet.num_zeros) )
268 if u: text += "[%s unconfirmed]"%( format_satoshis(u,True,self.wallet.num_zeros).strip() )
269 icon = QIcon(":icons/status_connected.png")
271 text = _( "Not connected" )
272 icon = QIcon(":icons/status_disconnected.png")
275 text = _( "Not enough funds" )
277 self.statusBar().showMessage(text)
278 self.status_button.setIcon( icon )
280 if self.wallet.up_to_date:
281 self.textbox.setText( self.wallet.banner )
282 self.update_history_tab()
283 self.update_receive_tab()
284 self.update_contacts_tab()
285 self.update_completions()
288 def create_history_tab(self):
289 self.history_list = l = MyTreeWidget(self)
291 l.setColumnWidth(0, 40)
292 l.setColumnWidth(1, 140)
293 l.setColumnWidth(2, 350)
294 l.setColumnWidth(3, 140)
295 l.setColumnWidth(4, 140)
296 l.setHeaderLabels( [ '', _( 'Date' ), _( 'To / From' ) , _('Amount'), _('Balance')] )
297 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.tx_label_clicked)
298 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), self.tx_label_changed)
299 l.setContextMenuPolicy(Qt.CustomContextMenu)
300 l.customContextMenuRequested.connect(self.create_history_menu)
303 def create_history_menu(self, position):
304 self.history_list.selectedIndexes()
305 item = self.history_list.currentItem()
307 tx_hash = str(item.toolTip(0))
309 menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash))
310 menu.addAction(_("Details"), lambda: self.tx_details(tx_hash))
311 menu.addAction(_("Edit description"), lambda: self.tx_label_clicked(item,2))
312 menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
314 def tx_details(self, tx_hash):
315 tx = self.wallet.tx_history.get(tx_hash)
318 conf = self.wallet.blocks - tx['height'] + 1
319 time_str = datetime.datetime.fromtimestamp( tx['timestamp']).isoformat(' ')[:-3]
324 tx_details = _("Transaction Details") +"\n\n" \
325 + "Transaction ID:\n" + tx_hash + "\n\n" \
326 + "Status: %d confirmations\n\n"%conf \
327 + "Date: %s\n\n"%time_str \
328 + "Inputs:\n-"+ '\n-'.join(tx['inputs']) + "\n\n" \
329 + "Outputs:\n-"+ '\n-'.join(tx['outputs'])
331 r = self.wallet.receipts.get(tx_hash)
333 tx_details += "\n_______________________________________" \
334 + '\n\nSigned URI: ' + r[2] \
335 + "\n\nSigned by: " + r[0] \
336 + '\n\nSignature: ' + r[1]
338 QMessageBox.information(self, 'Details', tx_details, 'OK')
341 def tx_label_clicked(self, item, column):
342 if column==2 and item.isSelected():
343 tx_hash = str(item.toolTip(0))
345 #if not self.wallet.labels.get(tx_hash): item.setText(2,'')
346 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
347 self.history_list.editItem( item, column )
348 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
351 def tx_label_changed(self, item, column):
355 tx_hash = str(item.toolTip(0))
356 tx = self.wallet.tx_history.get(tx_hash)
357 s = self.wallet.labels.get(tx_hash)
358 text = unicode( item.text(2) )
360 self.wallet.labels[tx_hash] = text
361 item.setForeground(2, QBrush(QColor('black')))
363 if s: self.wallet.labels.pop(tx_hash)
364 text = tx['default_label']
365 item.setText(2, text)
366 item.setForeground(2, QBrush(QColor('gray')))
369 def edit_label(self, is_recv):
370 l = self.receive_list if is_recv else self.contacts_list
371 c = 2 if is_recv else 1
372 item = l.currentItem()
373 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
374 l.editItem( item, c )
375 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
377 def address_label_clicked(self, item, column, l, column_addr, column_label):
378 if column==column_label and item.isSelected():
379 addr = unicode( item.text(column_addr) )
380 label = unicode( item.text(column_label) )
381 if label in self.wallet.aliases.keys():
383 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
384 l.editItem( item, column )
385 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
387 def address_label_changed(self, item, column, l, column_addr, column_label):
388 addr = unicode( item.text(column_addr) )
389 text = unicode( item.text(column_label) )
391 if text not in self.wallet.aliases.keys():
392 self.wallet.labels[addr] = text
394 print_error("Error: This is one of your aliases")
395 label = self.wallet.labels.get(addr,'')
396 item.setText(column_label, QString(label))
398 s = self.wallet.labels.get(addr)
399 if s: self.wallet.labels.pop(addr)
401 self.update_history_tab()
402 self.update_completions()
404 def update_history_tab(self):
405 self.history_list.clear()
407 for tx in self.wallet.get_tx_history():
408 tx_hash = tx['tx_hash']
410 conf = self.wallet.blocks - tx['height'] + 1
411 time_str = datetime.datetime.fromtimestamp( tx['timestamp']).isoformat(' ')[:-3]
412 icon = QIcon(":icons/confirmed.png")
416 icon = QIcon(":icons/unconfirmed.png")
419 label = self.wallet.labels.get(tx_hash)
420 is_default_label = (label == '') or (label is None)
421 if is_default_label: label = tx['default_label']
423 item = QTreeWidgetItem( [ '', time_str, label, format_satoshis(v,True,self.wallet.num_zeros), format_satoshis(balance,False,self.wallet.num_zeros)] )
424 item.setFont(2, QFont(MONOSPACE_FONT))
425 item.setFont(3, QFont(MONOSPACE_FONT))
426 item.setFont(4, QFont(MONOSPACE_FONT))
427 item.setToolTip(0, tx_hash)
429 item.setForeground(2, QBrush(QColor('grey')))
431 item.setIcon(0, icon)
432 self.history_list.insertTopLevelItem(0,item)
434 self.history_list.setCurrentItem(self.history_list.topLevelItem(0))
437 def create_send_tab(self):
442 grid.setColumnMinimumWidth(3,300)
443 grid.setColumnStretch(5,1)
445 self.payto_e = QLineEdit()
446 grid.addWidget(QLabel(_('Pay to')), 1, 0)
447 grid.addWidget(self.payto_e, 1, 1, 1, 3)
450 qrcode = qrscanner.scan_qr()
451 if 'address' in qrcode:
452 self.payto_e.setText(qrcode['address'])
453 if 'amount' in qrcode:
454 self.amount_e.setText(str(qrcode['amount']))
455 if 'label' in qrcode:
456 self.message_e.setText(qrcode['label'])
457 if 'message' in qrcode:
458 self.message_e.setText("%s (%s)" % (self.message_e.text(), qrcode['message']))
461 if qrscanner.is_available():
462 b = QPushButton(_("Scan QR code"))
463 b.clicked.connect(fill_from_qr)
464 grid.addWidget(b, 1, 5)
466 grid.addWidget(HelpButton(_('Recipient of the funds.') + '\n\n' + _('You may enter a Bitcoin address, a label from your list of contacts (a list of completions will be proposed), or an alias (email-like address that forwards to a Bitcoin address)')), 1, 4)
468 completer = QCompleter()
469 completer.setCaseSensitivity(False)
470 self.payto_e.setCompleter(completer)
471 completer.setModel(self.completions)
473 self.message_e = QLineEdit()
474 grid.addWidget(QLabel(_('Description')), 2, 0)
475 grid.addWidget(self.message_e, 2, 1, 1, 3)
476 grid.addWidget(HelpButton(_('Description of the transaction (not mandatory).') + '\n\n' + _('The description is not sent to the recipient of the funds. It is stored in your wallet file, and displayed in the \'History\' tab.')), 2, 4)
478 self.amount_e = QLineEdit()
479 grid.addWidget(QLabel(_('Amount')), 3, 0)
480 grid.addWidget(self.amount_e, 3, 1, 1, 2)
481 grid.addWidget(HelpButton(
482 _('Amount to be sent.') + '\n\n' \
483 + _('The amount will be displayed in red if you do not have enough funds in your wallet. Note that if you have frozen some of your addresses, the available funds will be lower than your total balance.')), 3, 3)
485 self.fee_e = QLineEdit()
486 grid.addWidget(QLabel(_('Fee')), 4, 0)
487 grid.addWidget(self.fee_e, 4, 1, 1, 2)
488 grid.addWidget(HelpButton(
489 _('Bitcoin transactions are in general not free. A transaction fee is paid by the sender of the funds.') + '\n\n'\
490 + _('The amount of fee can be decided freely by the sender. However, transactions with low fees take more time to be processed.') + '\n\n'\
491 + _('A suggested fee is automatically added to this field. You may override it. The suggested fee increases with the size of the transaction.')), 4, 3)
493 b = EnterButton(_("Send"), self.do_send)
494 grid.addWidget(b, 6, 1)
496 b = EnterButton(_("Clear"),self.do_clear)
497 grid.addWidget(b, 6, 2)
499 self.payto_sig = QLabel('')
500 grid.addWidget(self.payto_sig, 7, 0, 1, 4)
502 QShortcut(QKeySequence("Up"), w, w.focusPreviousChild)
503 QShortcut(QKeySequence("Down"), w, w.focusNextChild)
512 def entry_changed( is_fee ):
513 self.funds_error = False
514 amount = numbify(self.amount_e)
515 fee = numbify(self.fee_e)
516 if not is_fee: fee = None
519 inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee )
521 self.fee_e.setText( str( Decimal( fee ) / 100000000 ) )
524 palette.setColor(self.amount_e.foregroundRole(), QColor('black'))
527 palette.setColor(self.amount_e.foregroundRole(), QColor('red'))
528 self.funds_error = True
529 self.amount_e.setPalette(palette)
530 self.fee_e.setPalette(palette)
532 self.amount_e.textChanged.connect(lambda: entry_changed(False) )
533 self.fee_e.textChanged.connect(lambda: entry_changed(True) )
538 def update_completions(self):
540 for addr,label in self.wallet.labels.items():
541 if addr in self.wallet.addressbook:
542 l.append( label + ' <' + addr + '>')
543 l = l + self.wallet.aliases.keys()
545 self.completions.setStringList(l)
551 label = unicode( self.message_e.text() )
552 r = unicode( self.payto_e.text() )
556 m1 = re.match(ALIAS_REGEXP, r)
557 # label or alias, with address in brackets
558 m2 = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
561 to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
565 to_address = m2.group(2)
569 if not self.wallet.is_valid(to_address):
570 QMessageBox.warning(self, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK'))
574 amount = int( Decimal( unicode( self.amount_e.text())) * 100000000 )
576 QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
579 fee = int( Decimal( unicode( self.fee_e.text())) * 100000000 )
581 QMessageBox.warning(self, _('Error'), _('Invalid Fee'), _('OK'))
584 if self.wallet.use_encryption:
585 password = self.password_dialog()
592 tx = self.wallet.mktx( to_address, amount, label, password, fee)
593 except BaseException, e:
594 self.show_message(str(e))
597 status, msg = self.wallet.sendtx( tx )
599 QMessageBox.information(self, '', _('Payment sent.')+'\n'+msg, _('OK'))
601 self.update_contacts_tab()
603 QMessageBox.warning(self, _('Error'), msg, _('OK'))
606 def set_url(self, url):
607 payto, amount, label, message, signature, identity, url = self.wallet.parse_url(url, self.show_message, self.question)
608 self.tabs.setCurrentIndex(1)
609 label = self.wallet.labels.get(payto)
610 m_addr = label + ' <'+ payto+'>' if label else payto
611 self.payto_e.setText(m_addr)
613 self.message_e.setText(message)
614 self.amount_e.setText(amount)
616 self.set_frozen(self.payto_e,True)
617 self.set_frozen(self.amount_e,True)
618 self.set_frozen(self.message_e,True)
619 self.payto_sig.setText( ' The bitcoin URI was signed by ' + identity )
621 self.payto_sig.setVisible(False)
624 self.payto_sig.setVisible(False)
625 for e in [self.payto_e, self.message_e, self.amount_e, self.fee_e]:
627 self.set_frozen(e,False)
629 def set_frozen(self,entry,frozen):
631 entry.setReadOnly(True)
632 entry.setFrame(False)
634 palette.setColor(entry.backgroundRole(), QColor('lightgray'))
635 entry.setPalette(palette)
637 entry.setReadOnly(False)
640 palette.setColor(entry.backgroundRole(), QColor('white'))
641 entry.setPalette(palette)
644 def toggle_freeze(self,addr):
646 if addr in self.wallet.frozen_addresses:
647 self.wallet.unfreeze(addr)
649 self.wallet.freeze(addr)
650 self.update_receive_tab()
652 def toggle_priority(self,addr):
654 if addr in self.wallet.prioritized_addresses:
655 self.wallet.unprioritize(addr)
657 self.wallet.prioritize(addr)
658 self.update_receive_tab()
661 def create_list_tab(self, headers):
662 "generic tab creation method"
663 l = MyTreeWidget(self)
664 l.setColumnCount( len(headers) )
665 l.setHeaderLabels( headers )
675 vbox.addWidget(buttons)
680 buttons.setLayout(hbox)
685 def create_receive_tab(self):
686 l,w,hbox = self.create_list_tab([_('Flags'), _('Address'), _('Label'), _('Balance'), _('Tx')])
687 l.setContextMenuPolicy(Qt.CustomContextMenu)
688 l.customContextMenuRequested.connect(self.create_receive_menu)
689 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,1,2))
690 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,1,2))
691 self.receive_list = l
692 self.receive_buttons_hbox = hbox
696 def create_contacts_tab(self):
697 l,w,hbox = self.create_list_tab([_('Address'), _('Label'), _('Tx')])
698 l.setContextMenuPolicy(Qt.CustomContextMenu)
699 l.customContextMenuRequested.connect(self.create_contact_menu)
700 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,0,1))
701 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,0,1))
702 self.contacts_list = l
703 self.contacts_buttons_hbox = hbox
704 hbox.addWidget(EnterButton(_("New"), self.new_contact_dialog))
709 def create_receive_menu(self, position):
710 # fixme: this function apparently has a side effect.
711 # if it is not called the menu pops up several times
712 #self.receive_list.selectedIndexes()
714 item = self.receive_list.itemAt(position)
716 addr = unicode(item.text(1))
718 menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
719 menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
720 menu.addAction(_("Edit label"), lambda: self.edit_label(True))
721 if self.wallet.expert_mode:
722 t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
723 menu.addAction(t, lambda: self.toggle_freeze(addr))
724 t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
725 menu.addAction(t, lambda: self.toggle_priority(addr))
726 menu.exec_(self.receive_list.viewport().mapToGlobal(position))
729 def payto(self, x, is_alias):
736 label = self.wallet.labels.get(addr)
737 m_addr = label + ' <' + addr + '>' if label else addr
738 self.tabs.setCurrentIndex(1)
739 self.payto_e.setText(m_addr)
740 self.amount_e.setFocus()
742 def delete_contact(self, x, is_alias):
743 if self.question("Do you want to remove %s from your list of contacts?"%x):
744 if not is_alias and x in self.wallet.addressbook:
745 self.wallet.addressbook.remove(x)
746 if x in self.wallet.labels.keys():
747 self.wallet.labels.pop(x)
748 elif is_alias and x in self.wallet.aliases:
749 self.wallet.aliases.pop(x)
750 self.update_history_tab()
751 self.update_contacts_tab()
752 self.update_completions()
754 def create_contact_menu(self, position):
755 # fixme: this function apparently has a side effect.
756 # if it is not called the menu pops up several times
757 #self.contacts_list.selectedIndexes()
759 item = self.contacts_list.itemAt(position)
761 addr = unicode(item.text(0))
762 label = unicode(item.text(1))
763 is_alias = label in self.wallet.aliases.keys()
764 x = label if is_alias else addr
766 menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
767 menu.addAction(_("Pay to"), lambda: self.payto(x, is_alias))
768 menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
770 menu.addAction(_("Edit label"), lambda: self.edit_label(False))
772 menu.addAction(_("View alias details"), lambda: self.show_contact_details(label))
773 menu.addAction(_("Delete"), lambda: self.delete_contact(x,is_alias))
774 menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
777 def update_receive_tab(self):
778 l = self.receive_list
780 l.setColumnHidden(0,not self.wallet.expert_mode)
781 l.setColumnHidden(3,not self.wallet.expert_mode)
782 l.setColumnHidden(4,not self.wallet.expert_mode)
783 l.setColumnWidth(0, 50)
784 l.setColumnWidth(1, 310)
785 l.setColumnWidth(2, 250)
786 l.setColumnWidth(3, 130)
787 l.setColumnWidth(4, 10)
791 for address in self.wallet.all_addresses():
793 if self.wallet.is_change(address) and not self.wallet.expert_mode:
796 label = self.wallet.labels.get(address,'')
798 h = self.wallet.history.get(address,[])
800 if not item['is_input'] : n=n+1
804 if address in self.wallet.addresses:
806 if gap > self.wallet.gap_limit:
809 if address in self.wallet.addresses:
812 c, u = self.wallet.get_addr_balance(address)
813 balance = format_satoshis( c + u, False, self.wallet.num_zeros )
814 flags = self.wallet.get_address_flags(address)
815 item = QTreeWidgetItem( [ flags, address, label, balance, tx] )
817 item.setFont(0, QFont(MONOSPACE_FONT))
818 item.setFont(1, QFont(MONOSPACE_FONT))
819 item.setFont(3, QFont(MONOSPACE_FONT))
820 if address in self.wallet.frozen_addresses:
821 item.setBackgroundColor(1, QColor('lightblue'))
822 elif address in self.wallet.prioritized_addresses:
823 item.setBackgroundColor(1, QColor('lightgreen'))
824 if is_red and address in self.wallet.addresses:
825 item.setBackgroundColor(1, QColor('red'))
826 l.addTopLevelItem(item)
828 # we use column 1 because column 0 may be hidden
829 l.setCurrentItem(l.topLevelItem(0),1)
831 def show_contact_details(self, m):
832 a = self.wallet.aliases.get(m)
834 if a[0] in self.wallet.authorities.keys():
835 s = self.wallet.authorities.get(a[0])
838 msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
839 QMessageBox.information(self, 'Alias', msg, 'OK')
841 def update_contacts_tab(self):
843 l = self.contacts_list
845 l.setColumnHidden(2, not self.wallet.expert_mode)
846 l.setColumnWidth(0, 350)
847 l.setColumnWidth(1, 330)
848 l.setColumnWidth(2, 100)
851 for alias, v in self.wallet.aliases.items():
853 alias_targets.append(target)
854 item = QTreeWidgetItem( [ target, alias, '-'] )
855 item.setBackgroundColor(0, QColor('lightgray'))
856 l.addTopLevelItem(item)
858 for address in self.wallet.addressbook:
859 if address in alias_targets: continue
860 label = self.wallet.labels.get(address,'')
862 for item in self.wallet.tx_history.values():
863 if address in item['outputs'] : n=n+1
865 item = QTreeWidgetItem( [ address, label, tx] )
866 item.setFont(0, QFont(MONOSPACE_FONT))
867 l.addTopLevelItem(item)
869 l.setCurrentItem(l.topLevelItem(0))
871 def create_wall_tab(self):
872 self.textbox = textbox = QTextEdit(self)
873 textbox.setFont(QFont(MONOSPACE_FONT))
874 textbox.setReadOnly(True)
877 def create_status_bar(self):
879 sb.setFixedHeight(35)
881 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) )
882 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) )
884 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/seed.png"), "Seed", lambda: self.show_seed_dialog(self.wallet, self) ) )
885 self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) )
886 sb.addPermanentWidget( self.status_button )
887 self.setStatusBar(sb)
889 def new_contact_dialog(self):
890 text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':')
891 address = unicode(text)
893 if self.wallet.is_valid(address):
894 self.wallet.addressbook.append(address)
896 self.update_contacts_tab()
897 self.update_history_tab()
898 self.update_completions()
900 QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
903 def show_seed_dialog(wallet, parent=None):
905 QMessageBox.information(parent, _('Message'),
906 _('No seed'), _('OK'))
909 if wallet.use_encryption:
910 password = parent.password_dialog()
917 seed = wallet.pw_decode(wallet.seed, password)
919 QMessageBox.warning(parent, _('Error'),
920 _('Incorrect Password'), _('OK'))
923 dialog = QDialog(None)
925 dialog.setWindowTitle(_("Seed"))
927 brainwallet = ' '.join(mnemonic.mn_encode(seed))
929 msg = _('<p>"%s"</p>'
930 "<p>If you memorise or write down these 12 words, you will always be able to recover your wallet.</p>"
931 "<p>This is called a 'BrainWallet'. The order of words is important. Case does not matter (capitals or lowercase).</p>") % brainwallet
932 main_text = QLabel(msg)
933 main_text.setWordWrap(True)
936 logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
943 copy_function = lambda: app.clipboard().setText(brainwallet)
944 copy_button = QPushButton(_("Copy to Clipboard"))
945 copy_button.clicked.connect(copy_function)
947 show_qr_function = lambda: ElectrumWindow.show_seed_qrcode(seed)
948 qr_button = QPushButton(_("View as QR Code"))
949 qr_button.clicked.connect(show_qr_function)
951 ok_button = QPushButton(_("OK"))
952 ok_button.clicked.connect(dialog.accept)
954 main_layout = QGridLayout()
955 main_layout.addWidget(logo, 0, 0)
956 main_layout.addWidget(main_text, 0, 1, 1, -1)
957 main_layout.addWidget(copy_button, 1, 1)
958 main_layout.addWidget(qr_button, 1, 2)
959 main_layout.addWidget(ok_button, 1, 3)
960 dialog.setLayout(main_layout)
965 def show_seed_qrcode(seed):
969 d.setWindowTitle(_("Seed"))
970 d.setMinimumSize(270, 300)
972 vbox.addWidget(QRCodeWidget(seed))
975 b = QPushButton(_("OK"))
977 b.clicked.connect(d.accept)
984 def show_address_qrcode(self,address):
985 if not address: return
988 d.setWindowTitle(address)
989 d.setMinimumSize(270, 350)
991 qrw = QRCodeWidget(address)
995 amount_e = QLineEdit()
996 hbox.addWidget(QLabel(_('Amount')))
997 hbox.addWidget(amount_e)
1000 #hbox = QHBoxLayout()
1001 #label_e = QLineEdit()
1002 #hbox.addWidget(QLabel('Label'))
1003 #hbox.addWidget(label_e)
1004 #vbox.addLayout(hbox)
1006 def amount_changed():
1007 amount = numbify(amount_e)
1008 #label = str( label_e.getText() )
1009 if amount is not None:
1010 qrw.set_addr('bitcoin:%s?amount=%s'%(address,str( Decimal(amount) /100000000)))
1012 qrw.set_addr( address )
1016 bmp.save_qrcode(qrw.qr, "qrcode.bmp")
1017 self.show_message(_("QR code saved to file") + " 'qrcode.bmp'")
1019 amount_e.textChanged.connect( amount_changed )
1021 hbox = QHBoxLayout()
1023 b = QPushButton(_("Save"))
1024 b.clicked.connect(do_save)
1026 b = QPushButton(_("Close"))
1028 b.clicked.connect(d.accept)
1030 vbox.addLayout(hbox)
1034 def question(self, msg):
1035 return QMessageBox.question(self, _('Message'), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
1037 def show_message(self, msg):
1038 QMessageBox.information(self, _('Message'), msg, _('OK'))
1040 def password_dialog(self ):
1047 vbox = QVBoxLayout()
1048 msg = _('Please enter your password')
1049 vbox.addWidget(QLabel(msg))
1051 grid = QGridLayout()
1053 grid.addWidget(QLabel(_('Password')), 1, 0)
1054 grid.addWidget(pw, 1, 1)
1055 vbox.addLayout(grid)
1057 vbox.addLayout(ok_cancel_buttons(d))
1060 if not d.exec_(): return
1061 return unicode(pw.text())
1068 def change_password_dialog( wallet, parent=None ):
1071 QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
1079 new_pw = QLineEdit()
1080 new_pw.setEchoMode(2)
1081 conf_pw = QLineEdit()
1082 conf_pw.setEchoMode(2)
1084 vbox = QVBoxLayout()
1086 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')
1088 msg = _("Please choose a password to encrypt your wallet keys.")+'\n'+_("Leave these fields empty if you want to disable encryption.")
1089 vbox.addWidget(QLabel(msg))
1091 grid = QGridLayout()
1094 if wallet.use_encryption:
1095 grid.addWidget(QLabel(_('Password')), 1, 0)
1096 grid.addWidget(pw, 1, 1)
1098 grid.addWidget(QLabel(_('New Password')), 2, 0)
1099 grid.addWidget(new_pw, 2, 1)
1101 grid.addWidget(QLabel(_('Confirm Password')), 3, 0)
1102 grid.addWidget(conf_pw, 3, 1)
1103 vbox.addLayout(grid)
1105 vbox.addLayout(ok_cancel_buttons(d))
1108 if not d.exec_(): return
1110 password = unicode(pw.text()) if wallet.use_encryption else None
1111 new_password = unicode(new_pw.text())
1112 new_password2 = unicode(conf_pw.text())
1115 seed = wallet.pw_decode( wallet.seed, password)
1117 QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
1120 if new_password != new_password2:
1121 QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
1124 wallet.update_password(seed, password, new_password)
1127 def seed_dialog(wallet, parent=None):
1131 vbox = QVBoxLayout()
1132 msg = _("Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet.")
1133 vbox.addWidget(QLabel(msg))
1135 grid = QGridLayout()
1138 seed_e = QLineEdit()
1139 grid.addWidget(QLabel(_('Seed or mnemonic')), 1, 0)
1140 grid.addWidget(seed_e, 1, 1)
1144 grid.addWidget(QLabel(_('Gap limit')), 2, 0)
1145 grid.addWidget(gap_e, 2, 1)
1146 gap_e.textChanged.connect(lambda: numbify(gap_e,True))
1147 vbox.addLayout(grid)
1149 vbox.addLayout(ok_cancel_buttons(d))
1152 if not d.exec_(): return
1155 gap = int(unicode(gap_e.text()))
1157 QMessageBox.warning(None, _('Error'), 'error', 'OK')
1161 seed = unicode(seed_e.text())
1164 print_error("Warning: Not hex, trying decode")
1166 seed = mnemonic.mn_decode( seed.split(' ') )
1168 QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK'))
1171 QMessageBox.warning(None, _('Error'), _('No seed'), 'OK')
1174 wallet.seed = str(seed)
1175 #print repr(wallet.seed)
1176 wallet.gap_limit = gap
1180 def set_expert_mode(self, b):
1181 self.wallet.expert_mode = b
1183 self.update_receive_tab()
1184 self.update_contacts_tab()
1185 # if self.wallet.seed:
1186 # self.nochange_cb.setHidden(not self.wallet.expert_mode)
1189 def settings_dialog(self):
1192 vbox = QVBoxLayout()
1193 msg = _('Here are the settings of your wallet.') + '\n'\
1194 + _('For more explanations, click on the help buttons next to each field.')
1197 label.setFixedWidth(250)
1198 label.setWordWrap(True)
1199 label.setAlignment(Qt.AlignJustify)
1200 vbox.addWidget(label)
1202 grid = QGridLayout()
1204 vbox.addLayout(grid)
1207 fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) )
1208 grid.addWidget(QLabel(_('Transaction fee')), 2, 0)
1209 grid.addWidget(fee_e, 2, 1)
1210 msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \
1211 + _('Recommended value') + ': 0.001'
1212 grid.addWidget(HelpButton(msg), 2, 2)
1213 fee_e.textChanged.connect(lambda: numbify(fee_e,False))
1216 nz_e.setText("%d"% self.wallet.num_zeros)
1217 grid.addWidget(QLabel(_('Display zeros')), 3, 0)
1218 msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
1219 grid.addWidget(HelpButton(msg), 3, 2)
1220 grid.addWidget(nz_e, 3, 1)
1221 nz_e.textChanged.connect(lambda: numbify(nz_e,True))
1223 cb = QCheckBox(_('Expert mode'))
1224 grid.addWidget(cb, 4, 0)
1225 cb.setChecked(self.wallet.expert_mode)
1227 if self.wallet.expert_mode:
1229 usechange_cb = QCheckBox(_('Use change addresses'))
1230 grid.addWidget(usechange_cb, 5, 0)
1231 usechange_cb.setChecked(self.wallet.use_change)
1232 grid.addWidget(HelpButton(_('Using a change addresses makes it more difficult for other people to track your transactions. ')), 5, 2)
1234 msg = _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \
1235 + _('You may increase it if you need more receiving addresses.') + '\n\n' \
1236 + _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \
1237 + _('Given the current status of your address sequence, the minimum gap limit you can use is: ') + '%d'%self.wallet.min_acceptable_gap() + '\n\n' \
1238 + _('Warning') + ': ' \
1239 + _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \
1240 + _('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'
1242 gap_e.setText("%d"% self.wallet.gap_limit)
1243 grid.addWidget(QLabel(_('Gap limit')), 6, 0)
1244 grid.addWidget(gap_e, 6, 1)
1245 grid.addWidget(HelpButton(msg), 6, 2)
1246 gap_e.textChanged.connect(lambda: numbify(nz_e,True))
1249 gui.addItems(['Lite', 'Qt'])
1250 cfg = SimpleConfig()
1251 gui.setCurrentIndex(gui.findText(cfg.config["gui"].capitalize()))
1252 grid.addWidget(QLabel(_('Default GUI') + ':'), 7, 0)
1253 grid.addWidget(gui, 7, 1)
1254 grid.addWidget(HelpButton(_('Select which GUI mode to use at start up. ')), 7, 2)
1256 vbox.addLayout(ok_cancel_buttons(d))
1260 if not d.exec_(): return
1262 fee = unicode(fee_e.text())
1264 fee = int( 100000000 * Decimal(fee) )
1266 QMessageBox.warning(self, _('Error'), _('Invalid value') +': %s'%fee, _('OK'))
1269 if self.wallet.fee != fee:
1270 self.wallet.fee = fee
1273 nz = unicode(nz_e.text())
1278 QMessageBox.warning(self, _('Error'), _('Invalid value')+':%s'%nz, _('OK'))
1281 if self.wallet.num_zeros != nz:
1282 self.wallet.num_zeros = nz
1283 self.update_history_tab()
1284 self.update_receive_tab()
1287 if self.wallet.expert_mode:
1289 self.wallet.use_change = usechange_cb.isChecked()
1292 n = int(gap_e.text())
1294 QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
1296 if self.wallet.gap_limit != n:
1297 r = self.wallet.change_gap_limit(n)
1299 self.update_receive_tab()
1301 QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
1303 cfg = SimpleConfig()
1304 cfg.set_key("gui", str(gui.currentText()).lower())
1307 self.set_expert_mode(cb.isChecked())
1311 def network_dialog(wallet, parent=None):
1312 interface = wallet.interface
1314 if interface.is_connected:
1315 status = _("Connected to")+" %s:%d\n%d blocks"%(interface.host, interface.port, wallet.blocks)
1317 status = _("Not connected")
1318 server = wallet.server
1321 status = _("Please choose a server.")
1322 server = random.choice( DEFAULT_SERVERS )
1324 if not wallet.interface.servers:
1326 for x in DEFAULT_SERVERS:
1327 h,port,protocol = x.split(':')
1328 servers_list.append( (h,[(protocol,port)] ) )
1330 servers_list = wallet.interface.servers
1333 for item in servers_list:
1337 protocol, port = item2
1343 d.setWindowTitle(_('Server'))
1344 d.setMinimumSize(375, 20)
1346 vbox = QVBoxLayout()
1349 hbox = QHBoxLayout()
1351 l.setPixmap(QPixmap(":icons/network.png"))
1353 hbox.addWidget(QLabel(status))
1355 vbox.addLayout(hbox)
1357 hbox = QHBoxLayout()
1358 host_line = QLineEdit()
1359 host_line.setText(server)
1360 hbox.addWidget(QLabel(_('Connect to') + ':'))
1361 hbox.addWidget(host_line)
1362 vbox.addLayout(hbox)
1364 hbox = QHBoxLayout()
1366 buttonGroup = QGroupBox(_("Protocol"))
1367 radio1 = QRadioButton("tcp", buttonGroup)
1368 radio2 = QRadioButton("http", buttonGroup)
1371 return unicode(host_line.text()).split(':')
1373 def set_button(protocol):
1375 radio1.setChecked(1)
1376 elif protocol == 'h':
1377 radio2.setChecked(1)
1379 def set_protocol(protocol):
1380 host = current_line()[0]
1382 if protocol not in pp.keys():
1383 protocol = pp.keys()[0]
1384 set_button(protocol)
1386 host_line.setText( host + ':' + port + ':' + protocol)
1388 radio1.clicked.connect(lambda x: set_protocol('t') )
1389 radio2.clicked.connect(lambda x: set_protocol('h') )
1391 set_button(current_line()[2])
1393 hbox.addWidget(QLabel(_('Protocol')+':'))
1394 hbox.addWidget(radio1)
1395 hbox.addWidget(radio2)
1397 vbox.addLayout(hbox)
1399 hbox = QHBoxLayout()
1400 proxy_mode = QComboBox()
1401 proxy_host = QLineEdit()
1402 proxy_host.setFixedWidth(200)
1403 proxy_port = QLineEdit()
1404 proxy_port.setFixedWidth(50)
1405 proxy_mode.addItems(['NONE', 'SOCKS4', 'SOCKS5', 'HTTP'])
1406 proxy_mode.setCurrentIndex(proxy_mode.findText(str(interface.proxy["mode"]).upper()))
1407 proxy_host.setText(interface.proxy["host"])
1408 proxy_port.setText(interface.proxy["port"])
1409 hbox.addWidget(QLabel(_('Proxy') + ':'))
1410 hbox.addWidget(proxy_mode)
1411 hbox.addWidget(proxy_host)
1412 hbox.addWidget(proxy_port)
1413 vbox.addLayout(hbox)
1415 hbox = QHBoxLayout()
1417 if wallet.interface.servers:
1418 label = _('Active Servers')
1420 label = _('Default Servers')
1422 servers_list_widget = QTreeWidget(parent)
1423 servers_list_widget.setHeaderLabels( [ label ] )
1424 servers_list_widget.setMaximumHeight(150)
1425 for host in plist.keys():
1426 servers_list_widget.addTopLevelItem(QTreeWidgetItem( [ host ] ))
1429 host = unicode(x.text(0))
1431 if 't' in pp.keys():
1434 protocol = pp.keys()[0]
1436 host_line.setText( host + ':' + port + ':' + protocol)
1437 set_button(protocol)
1439 servers_list_widget.connect(servers_list_widget, SIGNAL('itemClicked(QTreeWidgetItem*, int)'), do_set_line)
1440 vbox.addWidget(servers_list_widget)
1442 vbox.addLayout(ok_cancel_buttons(d))
1445 if not d.exec_(): return
1446 server = unicode( host_line.text() )
1449 cfg = SimpleConfig()
1450 cfg.config["proxy"] = { u'mode':unicode(proxy_mode.currentText()).lower(), u'host':unicode(proxy_host.text()), u'port':unicode(proxy_port.text()) }
1452 wallet.set_server(server, cfg.config["proxy"])
1453 except Exception as err:
1454 QMessageBox.information(None, _('Error'), str(err), _('OK'))
1462 def closeEvent(self, event):
1463 cfg = SimpleConfig()
1465 cfg.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()])
1472 def __init__(self, wallet, app=None):
1473 self.wallet = wallet
1475 self.app = QApplication(sys.argv)
1477 def server_list_changed(self):
1480 def waiting_dialog(self):
1486 w.setWindowTitle('Electrum')
1488 vbox = QVBoxLayout()
1493 if self.wallet.up_to_date:
1496 l.setText("Please wait...\nAddresses generated: %d\nKilobytes received: %.1f"\
1497 %(len(self.wallet.all_addresses()), self.wallet.interface.bytes_received/1024.))
1499 w.connect(s, QtCore.SIGNAL('timersignal'), f)
1500 self.wallet.interface.poke()
1505 def restore_or_create(self):
1507 msg = _("Wallet file not found.")+"\n"+_("Do you want to create a new wallet, or to restore an existing one?")
1508 r = QMessageBox.question(None, _('Message'), msg, _('Create'), _('Restore'), _('Cancel'), 0, 2)
1509 if r==2: return False
1511 is_recovery = (r==1)
1512 wallet = self.wallet
1513 # ask for the server.
1514 if not ElectrumWindow.network_dialog( wallet, parent=None ): return False
1517 wallet.new_seed(None)
1518 wallet.init_mpk( wallet.seed )
1519 wallet.up_to_date_event.clear()
1520 wallet.up_to_date = False
1521 self.waiting_dialog()
1522 # run a dialog indicating the seed, ask the user to remember it
1523 ElectrumWindow.show_seed_dialog(wallet)
1525 ElectrumWindow.change_password_dialog(wallet)
1527 # ask for seed and gap.
1528 if not ElectrumWindow.seed_dialog( wallet ): return False
1529 wallet.init_mpk( wallet.seed )
1530 wallet.up_to_date_event.clear()
1531 wallet.up_to_date = False
1532 self.waiting_dialog()
1533 if wallet.is_found():
1534 # history and addressbook
1535 wallet.update_tx_history()
1536 wallet.fill_addressbook()
1537 print "Recovery successful"
1540 QMessageBox.information(None, _('Error'), _("No transactions found for this seed"), _('OK'))
1548 w = ElectrumWindow(self.wallet)
1549 if url: w.set_url(url)