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()
207 self.setGeometry(100,100,840,400)
208 title = 'Electrum ' + self.wallet.electrum_version + ' - ' + self.wallet.path
209 if not self.wallet.seed: title += ' [seedless]'
210 self.setWindowTitle( title )
212 QShortcut(QKeySequence("Ctrl+W"), self, self.close)
213 QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
214 QShortcut(QKeySequence("Ctrl+PgUp"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() - 1 )%tabs.count() ))
215 QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() + 1 )%tabs.count() ))
217 self.connect(self, QtCore.SIGNAL('updatesignal'), self.update_wallet)
218 self.history_list.setFocus(True)
220 # dark magic fix by flatfly; https://bitcointalk.org/index.php?topic=73651.msg959913#msg959913
221 if platform.system() == 'Windows':
222 n = 3 if self.wallet.seed else 2
223 tabs.setCurrentIndex (n)
224 tabs.setCurrentIndex (0)
227 def connect_slots(self, sender):
229 self.connect(sender, QtCore.SIGNAL('timersignal'), self.check_recipient)
230 self.previous_payto_e=''
232 def check_recipient(self):
233 if self.payto_e.hasFocus():
235 r = unicode( self.payto_e.text() )
236 if r != self.previous_payto_e:
237 self.previous_payto_e = r
239 if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
241 to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
245 s = r + ' <' + to_address + '>'
246 self.payto_e.setText(s)
249 def update_callback(self):
250 self.emit(QtCore.SIGNAL('updatesignal'))
252 def update_wallet(self):
253 if self.wallet.interface and self.wallet.interface.is_connected:
254 if self.wallet.blocks == -1:
255 text = _( "Connecting..." )
256 icon = QIcon(":icons/status_disconnected.png")
257 elif self.wallet.blocks == 0:
258 text = _( "Server not ready" )
259 icon = QIcon(":icons/status_disconnected.png")
260 elif not self.wallet.up_to_date:
261 text = _( "Synchronizing..." )
262 icon = QIcon(":icons/status_waiting.png")
264 c, u = self.wallet.get_balance()
265 text = _( "Balance" ) + ": %s "%( format_satoshis(c,False,self.wallet.num_zeros) )
266 if u: text += "[%s unconfirmed]"%( format_satoshis(u,True,self.wallet.num_zeros).strip() )
267 icon = QIcon(":icons/status_connected.png")
269 text = _( "Not connected" )
270 icon = QIcon(":icons/status_disconnected.png")
273 text = _( "Not enough funds" )
275 self.statusBar().showMessage(text)
276 self.status_button.setIcon( icon )
278 if self.wallet.up_to_date:
279 self.textbox.setText( self.wallet.banner )
280 self.update_history_tab()
281 self.update_receive_tab()
282 self.update_contacts_tab()
283 self.update_completions()
286 def create_history_tab(self):
287 self.history_list = l = MyTreeWidget(self)
289 l.setColumnWidth(0, 40)
290 l.setColumnWidth(1, 140)
291 l.setColumnWidth(2, 350)
292 l.setColumnWidth(3, 140)
293 l.setColumnWidth(4, 140)
294 l.setHeaderLabels( [ '', _( 'Date' ), _( 'To / From' ) , _('Amount'), _('Balance')] )
295 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.tx_label_clicked)
296 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), self.tx_label_changed)
297 l.setContextMenuPolicy(Qt.CustomContextMenu)
298 l.customContextMenuRequested.connect(self.create_history_menu)
301 def create_history_menu(self, position):
302 self.history_list.selectedIndexes()
303 item = self.history_list.currentItem()
305 tx_hash = str(item.toolTip(0))
307 menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash))
308 menu.addAction(_("Details"), lambda: self.tx_details(tx_hash))
309 menu.addAction(_("Edit description"), lambda: self.tx_label_clicked(item,2))
310 menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
312 def tx_details(self, tx_hash):
313 tx = self.wallet.tx_history.get(tx_hash)
316 conf = self.wallet.blocks - tx['height'] + 1
317 time_str = datetime.datetime.fromtimestamp( tx['timestamp']).isoformat(' ')[:-3]
322 tx_details = _("Transaction Details") +"\n\n" \
323 + "Transaction ID:\n" + tx_hash + "\n\n" \
324 + "Status: %d confirmations\n\n"%conf \
325 + "Date: %s\n\n"%time_str \
326 + "Inputs:\n-"+ '\n-'.join(tx['inputs']) + "\n\n" \
327 + "Outputs:\n-"+ '\n-'.join(tx['outputs'])
329 r = self.wallet.receipts.get(tx_hash)
331 tx_details += "\n_______________________________________" \
332 + '\n\nSigned URI: ' + r[2] \
333 + "\n\nSigned by: " + r[0] \
334 + '\n\nSignature: ' + r[1]
336 QMessageBox.information(self, 'Details', tx_details, 'OK')
339 def tx_label_clicked(self, item, column):
340 if column==2 and item.isSelected():
341 tx_hash = str(item.toolTip(0))
343 #if not self.wallet.labels.get(tx_hash): item.setText(2,'')
344 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
345 self.history_list.editItem( item, column )
346 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
349 def tx_label_changed(self, item, column):
353 tx_hash = str(item.toolTip(0))
354 tx = self.wallet.tx_history.get(tx_hash)
355 s = self.wallet.labels.get(tx_hash)
356 text = unicode( item.text(2) )
358 self.wallet.labels[tx_hash] = text
359 item.setForeground(2, QBrush(QColor('black')))
361 if s: self.wallet.labels.pop(tx_hash)
362 text = tx['default_label']
363 item.setText(2, text)
364 item.setForeground(2, QBrush(QColor('gray')))
367 def edit_label(self, is_recv):
368 l = self.receive_list if is_recv else self.contacts_list
369 c = 2 if is_recv else 1
370 item = l.currentItem()
371 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
372 l.editItem( item, c )
373 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
375 def address_label_clicked(self, item, column, l, column_addr, column_label):
376 if column==column_label and item.isSelected():
377 addr = unicode( item.text(column_addr) )
378 label = unicode( item.text(column_label) )
379 if label in self.wallet.aliases.keys():
381 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
382 l.editItem( item, column )
383 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
385 def address_label_changed(self, item, column, l, column_addr, column_label):
386 addr = unicode( item.text(column_addr) )
387 text = unicode( item.text(column_label) )
389 if text not in self.wallet.aliases.keys():
390 self.wallet.labels[addr] = text
392 print_error("Error: This is one of your aliases")
393 label = self.wallet.labels.get(addr,'')
394 item.setText(column_label, QString(label))
396 s = self.wallet.labels.get(addr)
397 if s: self.wallet.labels.pop(addr)
399 self.update_history_tab()
400 self.update_completions()
402 def update_history_tab(self):
403 self.history_list.clear()
405 for tx in self.wallet.get_tx_history():
406 tx_hash = tx['tx_hash']
408 conf = self.wallet.blocks - tx['height'] + 1
409 time_str = datetime.datetime.fromtimestamp( tx['timestamp']).isoformat(' ')[:-3]
410 icon = QIcon(":icons/confirmed.png")
414 icon = QIcon(":icons/unconfirmed.png")
417 label = self.wallet.labels.get(tx_hash)
418 is_default_label = (label == '') or (label is None)
419 if is_default_label: label = tx['default_label']
421 item = QTreeWidgetItem( [ '', time_str, label, format_satoshis(v,True,self.wallet.num_zeros), format_satoshis(balance,False,self.wallet.num_zeros)] )
422 item.setFont(2, QFont(MONOSPACE_FONT))
423 item.setFont(3, QFont(MONOSPACE_FONT))
424 item.setFont(4, QFont(MONOSPACE_FONT))
425 item.setToolTip(0, tx_hash)
427 item.setForeground(2, QBrush(QColor('grey')))
429 item.setIcon(0, icon)
430 self.history_list.insertTopLevelItem(0,item)
432 self.history_list.setCurrentItem(self.history_list.topLevelItem(0))
435 def create_send_tab(self):
440 grid.setColumnMinimumWidth(3,300)
441 grid.setColumnStretch(5,1)
443 self.payto_e = QLineEdit()
444 grid.addWidget(QLabel(_('Pay to')), 1, 0)
445 grid.addWidget(self.payto_e, 1, 1, 1, 3)
448 qrcode = qrscanner.scan_qr()
449 if 'address' in qrcode:
450 self.payto_e.setText(qrcode['address'])
451 if 'amount' in qrcode:
452 self.amount_e.setText(str(qrcode['amount']))
453 if 'label' in qrcode:
454 self.message_e.setText(qrcode['label'])
455 if 'message' in qrcode:
456 self.message_e.setText("%s (%s)" % (self.message_e.text(), qrcode['message']))
459 if qrscanner.is_available():
460 b = QPushButton(_("Scan QR code"))
461 b.clicked.connect(fill_from_qr)
462 grid.addWidget(b, 1, 5)
464 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)
466 completer = QCompleter()
467 completer.setCaseSensitivity(False)
468 self.payto_e.setCompleter(completer)
469 completer.setModel(self.completions)
471 self.message_e = QLineEdit()
472 grid.addWidget(QLabel(_('Description')), 2, 0)
473 grid.addWidget(self.message_e, 2, 1, 1, 3)
474 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)
476 self.amount_e = QLineEdit()
477 grid.addWidget(QLabel(_('Amount')), 3, 0)
478 grid.addWidget(self.amount_e, 3, 1, 1, 2)
479 grid.addWidget(HelpButton(
480 _('Amount to be sent.') + '\n\n' \
481 + _('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)
483 self.fee_e = QLineEdit()
484 grid.addWidget(QLabel(_('Fee')), 4, 0)
485 grid.addWidget(self.fee_e, 4, 1, 1, 2)
486 grid.addWidget(HelpButton(
487 _('Bitcoin transactions are in general not free. A transaction fee is paid by the sender of the funds.') + '\n\n'\
488 + _('The amount of fee can be decided freely by the sender. However, transactions with low fees take more time to be processed.') + '\n\n'\
489 + _('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)
491 b = EnterButton(_("Send"), self.do_send)
492 grid.addWidget(b, 6, 1)
494 b = EnterButton(_("Clear"),self.do_clear)
495 grid.addWidget(b, 6, 2)
497 self.payto_sig = QLabel('')
498 grid.addWidget(self.payto_sig, 7, 0, 1, 4)
500 QShortcut(QKeySequence("Up"), w, w.focusPreviousChild)
501 QShortcut(QKeySequence("Down"), w, w.focusNextChild)
510 def entry_changed( is_fee ):
511 self.funds_error = False
512 amount = numbify(self.amount_e)
513 fee = numbify(self.fee_e)
514 if not is_fee: fee = None
517 inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee )
519 self.fee_e.setText( str( Decimal( fee ) / 100000000 ) )
522 palette.setColor(self.amount_e.foregroundRole(), QColor('black'))
525 palette.setColor(self.amount_e.foregroundRole(), QColor('red'))
526 self.funds_error = True
527 self.amount_e.setPalette(palette)
528 self.fee_e.setPalette(palette)
530 self.amount_e.textChanged.connect(lambda: entry_changed(False) )
531 self.fee_e.textChanged.connect(lambda: entry_changed(True) )
536 def update_completions(self):
538 for addr,label in self.wallet.labels.items():
539 if addr in self.wallet.addressbook:
540 l.append( label + ' <' + addr + '>')
541 l = l + self.wallet.aliases.keys()
543 self.completions.setStringList(l)
549 label = unicode( self.message_e.text() )
550 r = unicode( self.payto_e.text() )
554 m1 = re.match(ALIAS_REGEXP, r)
555 # label or alias, with address in brackets
556 m2 = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
559 to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
563 to_address = m2.group(2)
567 if not self.wallet.is_valid(to_address):
568 QMessageBox.warning(self, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK'))
572 amount = int( Decimal( unicode( self.amount_e.text())) * 100000000 )
574 QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
577 fee = int( Decimal( unicode( self.fee_e.text())) * 100000000 )
579 QMessageBox.warning(self, _('Error'), _('Invalid Fee'), _('OK'))
582 if self.wallet.use_encryption:
583 password = self.password_dialog()
590 tx = self.wallet.mktx( to_address, amount, label, password, fee)
591 except BaseException, e:
592 self.show_message(str(e))
595 status, msg = self.wallet.sendtx( tx )
597 QMessageBox.information(self, '', _('Payment sent.')+'\n'+msg, _('OK'))
599 self.update_contacts_tab()
601 QMessageBox.warning(self, _('Error'), msg, _('OK'))
604 def set_url(self, url):
605 payto, amount, label, message, signature, identity, url = self.wallet.parse_url(url, self.show_message, self.question)
606 self.tabs.setCurrentIndex(1)
607 label = self.wallet.labels.get(payto)
608 m_addr = label + ' <'+ payto+'>' if label else payto
609 self.payto_e.setText(m_addr)
611 self.message_e.setText(message)
612 self.amount_e.setText(amount)
614 self.set_frozen(self.payto_e,True)
615 self.set_frozen(self.amount_e,True)
616 self.set_frozen(self.message_e,True)
617 self.payto_sig.setText( ' The bitcoin URI was signed by ' + identity )
619 self.payto_sig.setVisible(False)
622 self.payto_sig.setVisible(False)
623 for e in [self.payto_e, self.message_e, self.amount_e, self.fee_e]:
625 self.set_frozen(e,False)
627 def set_frozen(self,entry,frozen):
629 entry.setReadOnly(True)
630 entry.setFrame(False)
632 palette.setColor(entry.backgroundRole(), QColor('lightgray'))
633 entry.setPalette(palette)
635 entry.setReadOnly(False)
638 palette.setColor(entry.backgroundRole(), QColor('white'))
639 entry.setPalette(palette)
642 def toggle_freeze(self,addr):
644 if addr in self.wallet.frozen_addresses:
645 self.wallet.unfreeze(addr)
647 self.wallet.freeze(addr)
648 self.update_receive_tab()
650 def toggle_priority(self,addr):
652 if addr in self.wallet.prioritized_addresses:
653 self.wallet.unprioritize(addr)
655 self.wallet.prioritize(addr)
656 self.update_receive_tab()
659 def create_list_tab(self, headers):
660 "generic tab creation method"
661 l = MyTreeWidget(self)
662 l.setColumnCount( len(headers) )
663 l.setHeaderLabels( headers )
673 vbox.addWidget(buttons)
678 buttons.setLayout(hbox)
683 def create_receive_tab(self):
684 l,w,hbox = self.create_list_tab([_('Flags'), _('Address'), _('Label'), _('Balance'), _('Tx')])
685 l.setContextMenuPolicy(Qt.CustomContextMenu)
686 l.customContextMenuRequested.connect(self.create_receive_menu)
687 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,1,2))
688 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,1,2))
689 self.receive_list = l
690 self.receive_buttons_hbox = hbox
694 def create_contacts_tab(self):
695 l,w,hbox = self.create_list_tab([_('Address'), _('Label'), _('Tx')])
696 l.setContextMenuPolicy(Qt.CustomContextMenu)
697 l.customContextMenuRequested.connect(self.create_contact_menu)
698 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,0,1))
699 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,0,1))
700 self.contacts_list = l
701 self.contacts_buttons_hbox = hbox
702 hbox.addWidget(EnterButton(_("New"), self.new_contact_dialog))
707 def create_receive_menu(self, position):
708 # fixme: this function apparently has a side effect.
709 # if it is not called the menu pops up several times
710 #self.receive_list.selectedIndexes()
712 item = self.receive_list.itemAt(position)
714 addr = unicode(item.text(1))
716 menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
717 menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
718 menu.addAction(_("Edit label"), lambda: self.edit_label(True))
719 if self.wallet.expert_mode:
720 t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
721 menu.addAction(t, lambda: self.toggle_freeze(addr))
722 t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
723 menu.addAction(t, lambda: self.toggle_priority(addr))
724 menu.exec_(self.receive_list.viewport().mapToGlobal(position))
727 def payto(self, x, is_alias):
734 label = self.wallet.labels.get(addr)
735 m_addr = label + ' <' + addr + '>' if label else addr
736 self.tabs.setCurrentIndex(1)
737 self.payto_e.setText(m_addr)
738 self.amount_e.setFocus()
740 def delete_contact(self, x, is_alias):
741 if self.question("Do you want to remove %s from your list of contacts?"%x):
742 if not is_alias and x in self.wallet.addressbook:
743 self.wallet.addressbook.remove(x)
744 if x in self.wallet.labels.keys():
745 self.wallet.labels.pop(x)
746 elif is_alias and x in self.wallet.aliases:
747 self.wallet.aliases.pop(x)
748 self.update_history_tab()
749 self.update_contacts_tab()
750 self.update_completions()
752 def create_contact_menu(self, position):
753 # fixme: this function apparently has a side effect.
754 # if it is not called the menu pops up several times
755 #self.contacts_list.selectedIndexes()
757 item = self.contacts_list.itemAt(position)
759 addr = unicode(item.text(0))
760 label = unicode(item.text(1))
761 is_alias = label in self.wallet.aliases.keys()
762 x = label if is_alias else addr
764 menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
765 menu.addAction(_("Pay to"), lambda: self.payto(x, is_alias))
766 menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
768 menu.addAction(_("Edit label"), lambda: self.edit_label(False))
770 menu.addAction(_("View alias details"), lambda: self.show_contact_details(label))
771 menu.addAction(_("Delete"), lambda: self.delete_contact(x,is_alias))
772 menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
775 def update_receive_tab(self):
776 l = self.receive_list
778 l.setColumnHidden(0,not self.wallet.expert_mode)
779 l.setColumnHidden(3,not self.wallet.expert_mode)
780 l.setColumnHidden(4,not self.wallet.expert_mode)
781 l.setColumnWidth(0, 50)
782 l.setColumnWidth(1, 310)
783 l.setColumnWidth(2, 250)
784 l.setColumnWidth(3, 130)
785 l.setColumnWidth(4, 10)
789 for address in self.wallet.all_addresses():
791 if self.wallet.is_change(address) and not self.wallet.expert_mode:
794 label = self.wallet.labels.get(address,'')
796 h = self.wallet.history.get(address,[])
798 if not item['is_input'] : n=n+1
802 if address in self.wallet.addresses:
804 if gap > self.wallet.gap_limit:
807 if address in self.wallet.addresses:
810 c, u = self.wallet.get_addr_balance(address)
811 balance = format_satoshis( c + u, False, self.wallet.num_zeros )
812 flags = self.wallet.get_address_flags(address)
813 item = QTreeWidgetItem( [ flags, address, label, balance, tx] )
815 item.setFont(0, QFont(MONOSPACE_FONT))
816 item.setFont(1, QFont(MONOSPACE_FONT))
817 item.setFont(3, QFont(MONOSPACE_FONT))
818 if address in self.wallet.frozen_addresses:
819 item.setBackgroundColor(1, QColor('lightblue'))
820 elif address in self.wallet.prioritized_addresses:
821 item.setBackgroundColor(1, QColor('lightgreen'))
822 if is_red and address in self.wallet.addresses:
823 item.setBackgroundColor(1, QColor('red'))
824 l.addTopLevelItem(item)
826 # we use column 1 because column 0 may be hidden
827 l.setCurrentItem(l.topLevelItem(0),1)
829 def show_contact_details(self, m):
830 a = self.wallet.aliases.get(m)
832 if a[0] in self.wallet.authorities.keys():
833 s = self.wallet.authorities.get(a[0])
836 msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
837 QMessageBox.information(self, 'Alias', msg, 'OK')
839 def update_contacts_tab(self):
841 l = self.contacts_list
843 l.setColumnHidden(2, not self.wallet.expert_mode)
844 l.setColumnWidth(0, 350)
845 l.setColumnWidth(1, 330)
846 l.setColumnWidth(2, 100)
849 for alias, v in self.wallet.aliases.items():
851 alias_targets.append(target)
852 item = QTreeWidgetItem( [ target, alias, '-'] )
853 item.setBackgroundColor(0, QColor('lightgray'))
854 l.addTopLevelItem(item)
856 for address in self.wallet.addressbook:
857 if address in alias_targets: continue
858 label = self.wallet.labels.get(address,'')
860 for item in self.wallet.tx_history.values():
861 if address in item['outputs'] : n=n+1
863 item = QTreeWidgetItem( [ address, label, tx] )
864 item.setFont(0, QFont(MONOSPACE_FONT))
865 l.addTopLevelItem(item)
867 l.setCurrentItem(l.topLevelItem(0))
869 def create_wall_tab(self):
870 self.textbox = textbox = QTextEdit(self)
871 textbox.setFont(QFont(MONOSPACE_FONT))
872 textbox.setReadOnly(True)
875 def create_status_bar(self):
877 sb.setFixedHeight(35)
879 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) )
880 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) )
882 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/seed.png"), "Seed", lambda: self.show_seed_dialog(self.wallet, self) ) )
883 self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) )
884 sb.addPermanentWidget( self.status_button )
885 self.setStatusBar(sb)
887 def new_contact_dialog(self):
888 text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':')
889 address = unicode(text)
891 if self.wallet.is_valid(address):
892 self.wallet.addressbook.append(address)
894 self.update_contacts_tab()
895 self.update_history_tab()
896 self.update_completions()
898 QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
901 def show_seed_dialog(wallet, parent=None):
903 QMessageBox.information(parent, _('Message'),
904 _('No seed'), _('OK'))
907 if wallet.use_encryption:
908 password = parent.password_dialog()
915 seed = wallet.pw_decode(wallet.seed, password)
917 QMessageBox.warning(parent, _('Error'),
918 _('Incorrect Password'), _('OK'))
921 dialog = QDialog(None)
923 dialog.setWindowTitle(_("Seed"))
925 brainwallet = ' '.join(mnemonic.mn_encode(seed))
927 msg = _('<p>"%s"</p>'
928 "<p>If you memorise or write down these 12 words, you will always be able to recover your wallet.</p>"
929 "<p>This is called a 'BrainWallet'. The order of words is important. Case does not matter (capitals or lowercase).</p>") % brainwallet
930 main_text = QLabel(msg)
931 main_text.setWordWrap(True)
934 logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
941 copy_function = lambda: app.clipboard().setText(brainwallet)
942 copy_button = QPushButton(_("Copy to Clipboard"))
943 copy_button.clicked.connect(copy_function)
945 show_qr_function = lambda: ElectrumWindow.show_seed_qrcode(seed)
946 qr_button = QPushButton(_("View as QR Code"))
947 qr_button.clicked.connect(show_qr_function)
949 ok_button = QPushButton(_("OK"))
950 ok_button.clicked.connect(dialog.accept)
952 main_layout = QGridLayout()
953 main_layout.addWidget(logo, 0, 0)
954 main_layout.addWidget(main_text, 0, 1, 1, -1)
955 main_layout.addWidget(copy_button, 1, 1)
956 main_layout.addWidget(qr_button, 1, 2)
957 main_layout.addWidget(ok_button, 1, 3)
958 dialog.setLayout(main_layout)
963 def show_seed_qrcode(seed):
967 d.setWindowTitle(_("Seed"))
968 d.setMinimumSize(270, 300)
970 vbox.addWidget(QRCodeWidget(seed))
973 b = QPushButton(_("OK"))
975 b.clicked.connect(d.accept)
982 def show_address_qrcode(self,address):
983 if not address: return
986 d.setWindowTitle(address)
987 d.setMinimumSize(270, 350)
989 qrw = QRCodeWidget(address)
993 amount_e = QLineEdit()
994 hbox.addWidget(QLabel(_('Amount')))
995 hbox.addWidget(amount_e)
998 #hbox = QHBoxLayout()
999 #label_e = QLineEdit()
1000 #hbox.addWidget(QLabel('Label'))
1001 #hbox.addWidget(label_e)
1002 #vbox.addLayout(hbox)
1004 def amount_changed():
1005 amount = numbify(amount_e)
1006 #label = str( label_e.getText() )
1007 if amount is not None:
1008 qrw.set_addr('bitcoin:%s?amount=%s'%(address,str( Decimal(amount) /100000000)))
1010 qrw.set_addr( address )
1014 bmp.save_qrcode(qrw.qr, "qrcode.bmp")
1015 self.show_message(_("QR code saved to file") + " 'qrcode.bmp'")
1017 amount_e.textChanged.connect( amount_changed )
1019 hbox = QHBoxLayout()
1021 b = QPushButton(_("Save"))
1022 b.clicked.connect(do_save)
1024 b = QPushButton(_("Close"))
1026 b.clicked.connect(d.accept)
1028 vbox.addLayout(hbox)
1032 def question(self, msg):
1033 return QMessageBox.question(self, _('Message'), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
1035 def show_message(self, msg):
1036 QMessageBox.information(self, _('Message'), msg, _('OK'))
1038 def password_dialog(self ):
1045 vbox = QVBoxLayout()
1046 msg = _('Please enter your password')
1047 vbox.addWidget(QLabel(msg))
1049 grid = QGridLayout()
1051 grid.addWidget(QLabel(_('Password')), 1, 0)
1052 grid.addWidget(pw, 1, 1)
1053 vbox.addLayout(grid)
1055 vbox.addLayout(ok_cancel_buttons(d))
1058 if not d.exec_(): return
1059 return unicode(pw.text())
1066 def change_password_dialog( wallet, parent=None ):
1069 QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
1077 new_pw = QLineEdit()
1078 new_pw.setEchoMode(2)
1079 conf_pw = QLineEdit()
1080 conf_pw.setEchoMode(2)
1082 vbox = QVBoxLayout()
1084 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')
1086 msg = _("Please choose a password to encrypt your wallet keys.")+'\n'+_("Leave these fields empty if you want to disable encryption.")
1087 vbox.addWidget(QLabel(msg))
1089 grid = QGridLayout()
1092 if wallet.use_encryption:
1093 grid.addWidget(QLabel(_('Password')), 1, 0)
1094 grid.addWidget(pw, 1, 1)
1096 grid.addWidget(QLabel(_('New Password')), 2, 0)
1097 grid.addWidget(new_pw, 2, 1)
1099 grid.addWidget(QLabel(_('Confirm Password')), 3, 0)
1100 grid.addWidget(conf_pw, 3, 1)
1101 vbox.addLayout(grid)
1103 vbox.addLayout(ok_cancel_buttons(d))
1106 if not d.exec_(): return
1108 password = unicode(pw.text()) if wallet.use_encryption else None
1109 new_password = unicode(new_pw.text())
1110 new_password2 = unicode(conf_pw.text())
1113 seed = wallet.pw_decode( wallet.seed, password)
1115 QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
1118 if new_password != new_password2:
1119 QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
1122 wallet.update_password(seed, password, new_password)
1125 def seed_dialog(wallet, parent=None):
1129 vbox = QVBoxLayout()
1130 msg = _("Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet.")
1131 vbox.addWidget(QLabel(msg))
1133 grid = QGridLayout()
1136 seed_e = QLineEdit()
1137 grid.addWidget(QLabel(_('Seed or mnemonic')), 1, 0)
1138 grid.addWidget(seed_e, 1, 1)
1142 grid.addWidget(QLabel(_('Gap limit')), 2, 0)
1143 grid.addWidget(gap_e, 2, 1)
1144 gap_e.textChanged.connect(lambda: numbify(gap_e,True))
1145 vbox.addLayout(grid)
1147 vbox.addLayout(ok_cancel_buttons(d))
1150 if not d.exec_(): return
1153 gap = int(unicode(gap_e.text()))
1155 QMessageBox.warning(None, _('Error'), 'error', 'OK')
1159 seed = unicode(seed_e.text())
1162 print_error("Warning: Not hex, trying decode")
1164 seed = mnemonic.mn_decode( seed.split(' ') )
1166 QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK'))
1169 QMessageBox.warning(None, _('Error'), _('No seed'), 'OK')
1172 wallet.seed = str(seed)
1173 #print repr(wallet.seed)
1174 wallet.gap_limit = gap
1178 def set_expert_mode(self, b):
1179 self.wallet.expert_mode = b
1181 self.update_receive_tab()
1182 self.update_contacts_tab()
1183 # if self.wallet.seed:
1184 # self.nochange_cb.setHidden(not self.wallet.expert_mode)
1187 def settings_dialog(self):
1190 vbox = QVBoxLayout()
1191 msg = _('Here are the settings of your wallet.') + '\n'\
1192 + _('For more explanations, click on the help buttons next to each field.')
1195 label.setFixedWidth(250)
1196 label.setWordWrap(True)
1197 label.setAlignment(Qt.AlignJustify)
1198 vbox.addWidget(label)
1200 grid = QGridLayout()
1202 vbox.addLayout(grid)
1205 fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) )
1206 grid.addWidget(QLabel(_('Transaction fee')), 2, 0)
1207 grid.addWidget(fee_e, 2, 1)
1208 msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \
1209 + _('Recommended value') + ': 0.001'
1210 grid.addWidget(HelpButton(msg), 2, 2)
1211 fee_e.textChanged.connect(lambda: numbify(fee_e,False))
1214 nz_e.setText("%d"% self.wallet.num_zeros)
1215 grid.addWidget(QLabel(_('Display zeros')), 3, 0)
1216 msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
1217 grid.addWidget(HelpButton(msg), 3, 2)
1218 grid.addWidget(nz_e, 3, 1)
1219 nz_e.textChanged.connect(lambda: numbify(nz_e,True))
1221 cb = QCheckBox(_('Expert mode'))
1222 grid.addWidget(cb, 4, 0)
1223 cb.setChecked(self.wallet.expert_mode)
1225 if self.wallet.expert_mode:
1227 usechange_cb = QCheckBox(_('Use change addresses'))
1228 grid.addWidget(usechange_cb, 5, 0)
1229 usechange_cb.setChecked(self.wallet.use_change)
1230 grid.addWidget(HelpButton(_('Using a change addresses makes it more difficult for other people to track your transactions. ')), 5, 2)
1232 msg = _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \
1233 + _('You may increase it if you need more receiving addresses.') + '\n\n' \
1234 + _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \
1235 + _('Given the current status of your address sequence, the minimum gap limit you can use is: ') + '%d'%self.wallet.min_acceptable_gap() + '\n\n' \
1236 + _('Warning') + ': ' \
1237 + _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \
1238 + _('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'
1240 gap_e.setText("%d"% self.wallet.gap_limit)
1241 grid.addWidget(QLabel(_('Gap limit')), 6, 0)
1242 grid.addWidget(gap_e, 6, 1)
1243 grid.addWidget(HelpButton(msg), 6, 2)
1244 gap_e.textChanged.connect(lambda: numbify(nz_e,True))
1247 gui.addItems(['Lite', 'Qt'])
1248 cfg = SimpleConfig()
1249 gui.setCurrentIndex(gui.findText(cfg.config["gui"].capitalize()))
1250 grid.addWidget(QLabel(_('Default GUI') + ':'), 7, 0)
1251 grid.addWidget(gui, 7, 1)
1252 grid.addWidget(HelpButton(_('Select which GUI mode to use at start up. ')), 7, 2)
1254 vbox.addLayout(ok_cancel_buttons(d))
1258 if not d.exec_(): return
1260 fee = unicode(fee_e.text())
1262 fee = int( 100000000 * Decimal(fee) )
1264 QMessageBox.warning(self, _('Error'), _('Invalid value') +': %s'%fee, _('OK'))
1267 if self.wallet.fee != fee:
1268 self.wallet.fee = fee
1271 nz = unicode(nz_e.text())
1276 QMessageBox.warning(self, _('Error'), _('Invalid value')+':%s'%nz, _('OK'))
1279 if self.wallet.num_zeros != nz:
1280 self.wallet.num_zeros = nz
1281 self.update_history_tab()
1282 self.update_receive_tab()
1285 if self.wallet.expert_mode:
1287 self.wallet.use_change = usechange_cb.isChecked()
1290 n = int(gap_e.text())
1292 QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
1294 if self.wallet.gap_limit != n:
1295 r = self.wallet.change_gap_limit(n)
1297 self.update_receive_tab()
1299 QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
1301 cfg = SimpleConfig()
1302 cfg.config["gui"] = str(gui.currentText()).lower()
1305 self.set_expert_mode(cb.isChecked())
1309 def network_dialog(wallet, parent=None):
1310 interface = wallet.interface
1312 if interface.is_connected:
1313 status = _("Connected to")+" %s:%d\n%d blocks"%(interface.host, interface.port, wallet.blocks)
1315 status = _("Not connected")
1316 server = wallet.server
1319 status = _("Please choose a server.")
1320 server = random.choice( DEFAULT_SERVERS )
1322 if not wallet.interface.servers:
1324 for x in DEFAULT_SERVERS:
1325 h,port,protocol = x.split(':')
1326 servers_list.append( (h,[(protocol,port)] ) )
1328 servers_list = wallet.interface.servers
1331 for item in servers_list:
1335 protocol, port = item2
1341 d.setWindowTitle(_('Server'))
1342 d.setMinimumSize(375, 20)
1344 vbox = QVBoxLayout()
1347 hbox = QHBoxLayout()
1349 l.setPixmap(QPixmap(":icons/network.png"))
1351 hbox.addWidget(QLabel(status))
1353 vbox.addLayout(hbox)
1355 hbox = QHBoxLayout()
1356 host_line = QLineEdit()
1357 host_line.setText(server)
1358 hbox.addWidget(QLabel(_('Connect to') + ':'))
1359 hbox.addWidget(host_line)
1360 vbox.addLayout(hbox)
1362 hbox = QHBoxLayout()
1364 buttonGroup = QGroupBox(_("Protocol"))
1365 radio1 = QRadioButton("tcp", buttonGroup)
1366 radio2 = QRadioButton("http", buttonGroup)
1369 return unicode(host_line.text()).split(':')
1371 def set_button(protocol):
1373 radio1.setChecked(1)
1374 elif protocol == 'h':
1375 radio2.setChecked(1)
1377 def set_protocol(protocol):
1378 host = current_line()[0]
1380 if protocol not in pp.keys():
1381 protocol = pp.keys()[0]
1382 set_button(protocol)
1384 host_line.setText( host + ':' + port + ':' + protocol)
1386 radio1.clicked.connect(lambda x: set_protocol('t') )
1387 radio2.clicked.connect(lambda x: set_protocol('h') )
1389 set_button(current_line()[2])
1391 hbox.addWidget(QLabel(_('Protocol')+':'))
1392 hbox.addWidget(radio1)
1393 hbox.addWidget(radio2)
1395 vbox.addLayout(hbox)
1397 hbox = QHBoxLayout()
1398 proxy_mode = QComboBox()
1399 proxy_host = QLineEdit()
1400 proxy_host.setFixedWidth(200)
1401 proxy_port = QLineEdit()
1402 proxy_port.setFixedWidth(50)
1403 proxy_mode.addItems(['NONE', 'SOCKS4', 'SOCKS5', 'HTTP'])
1404 proxy_mode.setCurrentIndex(proxy_mode.findText(str(interface.proxy["mode"]).upper()))
1405 proxy_host.setText(interface.proxy["host"])
1406 proxy_port.setText(interface.proxy["port"])
1407 hbox.addWidget(QLabel(_('Proxy') + ':'))
1408 hbox.addWidget(proxy_mode)
1409 hbox.addWidget(proxy_host)
1410 hbox.addWidget(proxy_port)
1411 vbox.addLayout(hbox)
1413 hbox = QHBoxLayout()
1415 if wallet.interface.servers:
1416 label = _('Active Servers')
1418 label = _('Default Servers')
1420 servers_list_widget = QTreeWidget(parent)
1421 servers_list_widget.setHeaderLabels( [ label ] )
1422 servers_list_widget.setMaximumHeight(150)
1423 for host in plist.keys():
1424 servers_list_widget.addTopLevelItem(QTreeWidgetItem( [ host ] ))
1427 host = unicode(x.text(0))
1429 if 't' in pp.keys():
1432 protocol = pp.keys()[0]
1434 host_line.setText( host + ':' + port + ':' + protocol)
1435 set_button(protocol)
1437 servers_list_widget.connect(servers_list_widget, SIGNAL('itemClicked(QTreeWidgetItem*, int)'), do_set_line)
1438 vbox.addWidget(servers_list_widget)
1440 vbox.addLayout(ok_cancel_buttons(d))
1443 if not d.exec_(): return
1444 server = unicode( host_line.text() )
1447 cfg = SimpleConfig()
1448 cfg.config["proxy"] = { u'mode':unicode(proxy_mode.currentText()).lower(), u'host':unicode(proxy_host.text()), u'port':unicode(proxy_port.text()) }
1450 wallet.set_server(server, cfg.config["proxy"])
1451 except Exception as err:
1452 QMessageBox.information(None, _('Error'), str(err), _('OK'))
1464 def __init__(self, wallet, app=None):
1465 self.wallet = wallet
1467 self.app = QApplication(sys.argv)
1469 def server_list_changed(self):
1472 def waiting_dialog(self):
1478 w.setWindowTitle('Electrum')
1480 vbox = QVBoxLayout()
1485 if self.wallet.up_to_date:
1488 l.setText("Please wait...\nAddresses generated: %d\nKilobytes received: %.1f"\
1489 %(len(self.wallet.all_addresses()), self.wallet.interface.bytes_received/1024.))
1491 w.connect(s, QtCore.SIGNAL('timersignal'), f)
1492 self.wallet.interface.poke()
1497 def restore_or_create(self):
1499 msg = _("Wallet file not found.")+"\n"+_("Do you want to create a new wallet, or to restore an existing one?")
1500 r = QMessageBox.question(None, _('Message'), msg, _('Create'), _('Restore'), _('Cancel'), 0, 2)
1501 if r==2: return False
1503 is_recovery = (r==1)
1504 wallet = self.wallet
1505 # ask for the server.
1506 if not ElectrumWindow.network_dialog( wallet, parent=None ): return False
1509 wallet.new_seed(None)
1510 wallet.init_mpk( wallet.seed )
1511 wallet.up_to_date_event.clear()
1512 wallet.up_to_date = False
1513 self.waiting_dialog()
1514 # run a dialog indicating the seed, ask the user to remember it
1515 ElectrumWindow.show_seed_dialog(wallet)
1517 ElectrumWindow.change_password_dialog(wallet)
1519 # ask for seed and gap.
1520 if not ElectrumWindow.seed_dialog( wallet ): return False
1521 wallet.init_mpk( wallet.seed )
1522 wallet.up_to_date_event.clear()
1523 wallet.up_to_date = False
1524 self.waiting_dialog()
1525 if wallet.is_found():
1526 # history and addressbook
1527 wallet.update_tx_history()
1528 wallet.fill_addressbook()
1529 print "Recovery successful"
1532 QMessageBox.information(None, _('Error'), _("No transactions found for this seed"), _('OK'))
1540 w = ElectrumWindow(self.wallet)
1541 if url: w.set_url(url)