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 = _("Your wallet generation seed is") +":<p>\"" + brainwallet + "\"<p>" \
930 + _("Please write down or memorize these 12 words (order is important).") + " " \
931 + _("This seed will allow you to recover your wallet in case of computer failure.") + "<p>" \
932 + _("WARNING: Never disclose your seed. Never type it on a website.") + "<p>"
934 main_text = QLabel(msg)
935 main_text.setWordWrap(True)
938 logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
945 copy_function = lambda: app.clipboard().setText(brainwallet)
946 copy_button = QPushButton(_("Copy to Clipboard"))
947 copy_button.clicked.connect(copy_function)
949 show_qr_function = lambda: ElectrumWindow.show_seed_qrcode(seed)
950 qr_button = QPushButton(_("View as QR Code"))
951 qr_button.clicked.connect(show_qr_function)
953 ok_button = QPushButton(_("OK"))
954 ok_button.clicked.connect(dialog.accept)
956 main_layout = QGridLayout()
957 main_layout.addWidget(logo, 0, 0)
958 main_layout.addWidget(main_text, 0, 1, 1, -1)
959 main_layout.addWidget(copy_button, 1, 1)
960 main_layout.addWidget(qr_button, 1, 2)
961 main_layout.addWidget(ok_button, 1, 3)
962 dialog.setLayout(main_layout)
967 def show_seed_qrcode(seed):
971 d.setWindowTitle(_("Seed"))
972 d.setMinimumSize(270, 300)
974 vbox.addWidget(QRCodeWidget(seed))
977 b = QPushButton(_("OK"))
979 b.clicked.connect(d.accept)
986 def show_address_qrcode(self,address):
987 if not address: return
990 d.setWindowTitle(address)
991 d.setMinimumSize(270, 350)
993 qrw = QRCodeWidget(address)
997 amount_e = QLineEdit()
998 hbox.addWidget(QLabel(_('Amount')))
999 hbox.addWidget(amount_e)
1000 vbox.addLayout(hbox)
1002 #hbox = QHBoxLayout()
1003 #label_e = QLineEdit()
1004 #hbox.addWidget(QLabel('Label'))
1005 #hbox.addWidget(label_e)
1006 #vbox.addLayout(hbox)
1008 def amount_changed():
1009 amount = numbify(amount_e)
1010 #label = str( label_e.getText() )
1011 if amount is not None:
1012 qrw.set_addr('bitcoin:%s?amount=%s'%(address,str( Decimal(amount) /100000000)))
1014 qrw.set_addr( address )
1018 bmp.save_qrcode(qrw.qr, "qrcode.bmp")
1019 self.show_message(_("QR code saved to file") + " 'qrcode.bmp'")
1021 amount_e.textChanged.connect( amount_changed )
1023 hbox = QHBoxLayout()
1025 b = QPushButton(_("Save"))
1026 b.clicked.connect(do_save)
1028 b = QPushButton(_("Close"))
1030 b.clicked.connect(d.accept)
1032 vbox.addLayout(hbox)
1036 def question(self, msg):
1037 return QMessageBox.question(self, _('Message'), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
1039 def show_message(self, msg):
1040 QMessageBox.information(self, _('Message'), msg, _('OK'))
1042 def password_dialog(self ):
1049 vbox = QVBoxLayout()
1050 msg = _('Please enter your password')
1051 vbox.addWidget(QLabel(msg))
1053 grid = QGridLayout()
1055 grid.addWidget(QLabel(_('Password')), 1, 0)
1056 grid.addWidget(pw, 1, 1)
1057 vbox.addLayout(grid)
1059 vbox.addLayout(ok_cancel_buttons(d))
1062 if not d.exec_(): return
1063 return unicode(pw.text())
1070 def change_password_dialog( wallet, parent=None ):
1073 QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
1081 new_pw = QLineEdit()
1082 new_pw.setEchoMode(2)
1083 conf_pw = QLineEdit()
1084 conf_pw.setEchoMode(2)
1086 vbox = QVBoxLayout()
1088 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')
1090 msg = _("Please choose a password to encrypt your wallet keys.")+'\n'+_("Leave these fields empty if you want to disable encryption.")
1091 vbox.addWidget(QLabel(msg))
1093 grid = QGridLayout()
1096 if wallet.use_encryption:
1097 grid.addWidget(QLabel(_('Password')), 1, 0)
1098 grid.addWidget(pw, 1, 1)
1100 grid.addWidget(QLabel(_('New Password')), 2, 0)
1101 grid.addWidget(new_pw, 2, 1)
1103 grid.addWidget(QLabel(_('Confirm Password')), 3, 0)
1104 grid.addWidget(conf_pw, 3, 1)
1105 vbox.addLayout(grid)
1107 vbox.addLayout(ok_cancel_buttons(d))
1110 if not d.exec_(): return
1112 password = unicode(pw.text()) if wallet.use_encryption else None
1113 new_password = unicode(new_pw.text())
1114 new_password2 = unicode(conf_pw.text())
1117 seed = wallet.pw_decode( wallet.seed, password)
1119 QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
1122 if new_password != new_password2:
1123 QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
1126 wallet.update_password(seed, password, new_password)
1129 def seed_dialog(wallet, parent=None):
1133 vbox = QVBoxLayout()
1134 msg = _("Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet.")
1135 vbox.addWidget(QLabel(msg))
1137 grid = QGridLayout()
1140 seed_e = QLineEdit()
1141 grid.addWidget(QLabel(_('Seed or mnemonic')), 1, 0)
1142 grid.addWidget(seed_e, 1, 1)
1146 grid.addWidget(QLabel(_('Gap limit')), 2, 0)
1147 grid.addWidget(gap_e, 2, 1)
1148 gap_e.textChanged.connect(lambda: numbify(gap_e,True))
1149 vbox.addLayout(grid)
1151 vbox.addLayout(ok_cancel_buttons(d))
1154 if not d.exec_(): return
1157 gap = int(unicode(gap_e.text()))
1159 QMessageBox.warning(None, _('Error'), 'error', 'OK')
1163 seed = unicode(seed_e.text())
1166 print_error("Warning: Not hex, trying decode")
1168 seed = mnemonic.mn_decode( seed.split(' ') )
1170 QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK'))
1173 QMessageBox.warning(None, _('Error'), _('No seed'), 'OK')
1176 wallet.seed = str(seed)
1177 #print repr(wallet.seed)
1178 wallet.gap_limit = gap
1182 def set_expert_mode(self, b):
1183 self.wallet.expert_mode = b
1185 self.update_receive_tab()
1186 self.update_contacts_tab()
1187 # if self.wallet.seed:
1188 # self.nochange_cb.setHidden(not self.wallet.expert_mode)
1191 def settings_dialog(self):
1194 vbox = QVBoxLayout()
1195 msg = _('Here are the settings of your wallet.') + '\n'\
1196 + _('For more explanations, click on the help buttons next to each field.')
1199 label.setFixedWidth(250)
1200 label.setWordWrap(True)
1201 label.setAlignment(Qt.AlignJustify)
1202 vbox.addWidget(label)
1204 grid = QGridLayout()
1206 vbox.addLayout(grid)
1209 fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) )
1210 grid.addWidget(QLabel(_('Transaction fee')), 2, 0)
1211 grid.addWidget(fee_e, 2, 1)
1212 msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \
1213 + _('Recommended value') + ': 0.001'
1214 grid.addWidget(HelpButton(msg), 2, 2)
1215 fee_e.textChanged.connect(lambda: numbify(fee_e,False))
1218 nz_e.setText("%d"% self.wallet.num_zeros)
1219 grid.addWidget(QLabel(_('Display zeros')), 3, 0)
1220 msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
1221 grid.addWidget(HelpButton(msg), 3, 2)
1222 grid.addWidget(nz_e, 3, 1)
1223 nz_e.textChanged.connect(lambda: numbify(nz_e,True))
1225 cb = QCheckBox(_('Expert mode'))
1226 grid.addWidget(cb, 4, 0)
1227 cb.setChecked(self.wallet.expert_mode)
1229 if self.wallet.expert_mode:
1231 usechange_cb = QCheckBox(_('Use change addresses'))
1232 grid.addWidget(usechange_cb, 5, 0)
1233 usechange_cb.setChecked(self.wallet.use_change)
1234 grid.addWidget(HelpButton(_('Using a change addresses makes it more difficult for other people to track your transactions. ')), 5, 2)
1236 msg = _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \
1237 + _('You may increase it if you need more receiving addresses.') + '\n\n' \
1238 + _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \
1239 + _('Given the current status of your address sequence, the minimum gap limit you can use is: ') + '%d'%self.wallet.min_acceptable_gap() + '\n\n' \
1240 + _('Warning') + ': ' \
1241 + _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \
1242 + _('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'
1244 gap_e.setText("%d"% self.wallet.gap_limit)
1245 grid.addWidget(QLabel(_('Gap limit')), 6, 0)
1246 grid.addWidget(gap_e, 6, 1)
1247 grid.addWidget(HelpButton(msg), 6, 2)
1248 gap_e.textChanged.connect(lambda: numbify(nz_e,True))
1251 gui.addItems(['Lite', 'Qt'])
1252 cfg = SimpleConfig()
1253 gui.setCurrentIndex(gui.findText(cfg.config["gui"].capitalize()))
1254 grid.addWidget(QLabel(_('Default GUI') + ':'), 7, 0)
1255 grid.addWidget(gui, 7, 1)
1256 grid.addWidget(HelpButton(_('Select which GUI mode to use at start up. ')), 7, 2)
1258 vbox.addLayout(ok_cancel_buttons(d))
1262 if not d.exec_(): return
1264 fee = unicode(fee_e.text())
1266 fee = int( 100000000 * Decimal(fee) )
1268 QMessageBox.warning(self, _('Error'), _('Invalid value') +': %s'%fee, _('OK'))
1271 if self.wallet.fee != fee:
1272 self.wallet.fee = fee
1275 nz = unicode(nz_e.text())
1280 QMessageBox.warning(self, _('Error'), _('Invalid value')+':%s'%nz, _('OK'))
1283 if self.wallet.num_zeros != nz:
1284 self.wallet.num_zeros = nz
1285 self.update_history_tab()
1286 self.update_receive_tab()
1289 if self.wallet.expert_mode:
1291 self.wallet.use_change = usechange_cb.isChecked()
1294 n = int(gap_e.text())
1296 QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
1298 if self.wallet.gap_limit != n:
1299 r = self.wallet.change_gap_limit(n)
1301 self.update_receive_tab()
1303 QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
1305 cfg = SimpleConfig()
1306 cfg.set_key("gui", str(gui.currentText()).lower())
1309 self.set_expert_mode(cb.isChecked())
1313 def network_dialog(wallet, parent=None):
1314 interface = wallet.interface
1316 if interface.is_connected:
1317 status = _("Connected to")+" %s:%d\n%d blocks"%(interface.host, interface.port, wallet.blocks)
1319 status = _("Not connected")
1320 server = wallet.server
1323 status = _("Please choose a server.")
1324 server = random.choice( DEFAULT_SERVERS )
1326 if not wallet.interface.servers:
1328 for x in DEFAULT_SERVERS:
1329 h,port,protocol = x.split(':')
1330 servers_list.append( (h,[(protocol,port)] ) )
1332 servers_list = wallet.interface.servers
1335 for item in servers_list:
1339 protocol, port = item2
1345 d.setWindowTitle(_('Server'))
1346 d.setMinimumSize(375, 20)
1348 vbox = QVBoxLayout()
1351 hbox = QHBoxLayout()
1353 l.setPixmap(QPixmap(":icons/network.png"))
1355 hbox.addWidget(QLabel(status))
1357 vbox.addLayout(hbox)
1359 hbox = QHBoxLayout()
1360 host_line = QLineEdit()
1361 host_line.setText(server)
1362 hbox.addWidget(QLabel(_('Connect to') + ':'))
1363 hbox.addWidget(host_line)
1364 vbox.addLayout(hbox)
1366 hbox = QHBoxLayout()
1368 buttonGroup = QGroupBox(_("Protocol"))
1369 radio1 = QRadioButton("tcp", buttonGroup)
1370 radio2 = QRadioButton("http", buttonGroup)
1373 return unicode(host_line.text()).split(':')
1375 def set_button(protocol):
1377 radio1.setChecked(1)
1378 elif protocol == 'h':
1379 radio2.setChecked(1)
1381 def set_protocol(protocol):
1382 host = current_line()[0]
1384 if protocol not in pp.keys():
1385 protocol = pp.keys()[0]
1386 set_button(protocol)
1388 host_line.setText( host + ':' + port + ':' + protocol)
1390 radio1.clicked.connect(lambda x: set_protocol('t') )
1391 radio2.clicked.connect(lambda x: set_protocol('h') )
1393 set_button(current_line()[2])
1395 hbox.addWidget(QLabel(_('Protocol')+':'))
1396 hbox.addWidget(radio1)
1397 hbox.addWidget(radio2)
1399 vbox.addLayout(hbox)
1401 hbox = QHBoxLayout()
1402 proxy_mode = QComboBox()
1403 proxy_host = QLineEdit()
1404 proxy_host.setFixedWidth(200)
1405 proxy_port = QLineEdit()
1406 proxy_port.setFixedWidth(50)
1407 proxy_mode.addItems(['NONE', 'SOCKS4', 'SOCKS5', 'HTTP'])
1409 cfg = SimpleConfig()
1410 proxy_config = cfg.config['proxy']
1411 proxy_mode.setCurrentIndex(proxy_mode.findText(str(proxy_config["mode"]).upper()))
1412 proxy_host.setText(proxy_config["host"])
1413 proxy_port.setText(proxy_config["port"])
1414 hbox.addWidget(QLabel(_('Proxy') + ':'))
1415 hbox.addWidget(proxy_mode)
1416 hbox.addWidget(proxy_host)
1417 hbox.addWidget(proxy_port)
1418 vbox.addLayout(hbox)
1420 hbox = QHBoxLayout()
1422 if wallet.interface.servers:
1423 label = _('Active Servers')
1425 label = _('Default Servers')
1427 servers_list_widget = QTreeWidget(parent)
1428 servers_list_widget.setHeaderLabels( [ label ] )
1429 servers_list_widget.setMaximumHeight(150)
1430 for host in plist.keys():
1431 servers_list_widget.addTopLevelItem(QTreeWidgetItem( [ host ] ))
1434 host = unicode(x.text(0))
1436 if 't' in pp.keys():
1439 protocol = pp.keys()[0]
1441 host_line.setText( host + ':' + port + ':' + protocol)
1442 set_button(protocol)
1444 servers_list_widget.connect(servers_list_widget, SIGNAL('itemClicked(QTreeWidgetItem*, int)'), do_set_line)
1445 vbox.addWidget(servers_list_widget)
1447 vbox.addLayout(ok_cancel_buttons(d))
1450 if not d.exec_(): return
1451 server = unicode( host_line.text() )
1454 cfg.set_key("proxy", { u'mode':unicode(proxy_mode.currentText()).lower(), u'host':unicode(proxy_host.text()), u'port':unicode(proxy_port.text()) }, True)
1455 if cfg.config["proxy"]["mode"] != "none":
1456 wallet.set_server(server, cfg.config["proxy"])
1458 wallet.set_server(server)
1460 except Exception as err:
1461 QMessageBox.information(None, _('Error'), str(err), _('OK'))
1469 def closeEvent(self, event):
1470 cfg = SimpleConfig()
1472 cfg.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()])
1479 def __init__(self, wallet, app=None):
1480 self.wallet = wallet
1482 self.app = QApplication(sys.argv)
1484 def server_list_changed(self):
1487 def waiting_dialog(self):
1493 w.setWindowTitle('Electrum')
1495 vbox = QVBoxLayout()
1500 if self.wallet.up_to_date:
1503 l.setText("Please wait...\nAddresses generated: %d\nKilobytes received: %.1f"\
1504 %(len(self.wallet.all_addresses()), self.wallet.interface.bytes_received/1024.))
1506 w.connect(s, QtCore.SIGNAL('timersignal'), f)
1507 self.wallet.interface.poke()
1512 def restore_or_create(self):
1514 msg = _("Wallet file not found.")+"\n"+_("Do you want to create a new wallet, or to restore an existing one?")
1515 r = QMessageBox.question(None, _('Message'), msg, _('Create'), _('Restore'), _('Cancel'), 0, 2)
1516 if r==2: return False
1518 is_recovery = (r==1)
1519 wallet = self.wallet
1520 # ask for the server.
1521 if not ElectrumWindow.network_dialog( wallet, parent=None ): return False
1524 wallet.new_seed(None)
1525 wallet.init_mpk( wallet.seed )
1526 wallet.up_to_date_event.clear()
1527 wallet.up_to_date = False
1528 self.waiting_dialog()
1529 # run a dialog indicating the seed, ask the user to remember it
1530 ElectrumWindow.show_seed_dialog(wallet)
1532 ElectrumWindow.change_password_dialog(wallet)
1534 # ask for seed and gap.
1535 if not ElectrumWindow.seed_dialog( wallet ): return False
1536 wallet.init_mpk( wallet.seed )
1537 wallet.up_to_date_event.clear()
1538 wallet.up_to_date = False
1539 self.waiting_dialog()
1540 if wallet.is_found():
1541 # history and addressbook
1542 wallet.update_tx_history()
1543 wallet.fill_addressbook()
1544 print "Recovery successful"
1547 QMessageBox.information(None, _('Error'), _("No transactions found for this seed"), _('OK'))
1555 w = ElectrumWindow(self.wallet)
1556 if url: w.set_url(url)