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
693 self.details_button = EnterButton(self.details_button_text(), self.toggle_detailed_view)
694 hbox.addWidget(self.details_button)
698 def details_button_text(self):
699 return _('Hide details') if self.wallet.gui_detailed_view else _('Show details')
701 def toggle_detailed_view(self):
702 self.wallet.gui_detailed_view = not self.wallet.gui_detailed_view
703 self.details_button.setText(self.details_button_text())
705 self.update_receive_tab()
706 self.update_contacts_tab()
709 def create_contacts_tab(self):
710 l,w,hbox = self.create_list_tab([_('Address'), _('Label'), _('Tx')])
711 l.setContextMenuPolicy(Qt.CustomContextMenu)
712 l.customContextMenuRequested.connect(self.create_contact_menu)
713 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,0,1))
714 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,0,1))
715 self.contacts_list = l
716 self.contacts_buttons_hbox = hbox
717 hbox.addWidget(EnterButton(_("New"), self.new_contact_dialog))
722 def create_receive_menu(self, position):
723 # fixme: this function apparently has a side effect.
724 # if it is not called the menu pops up several times
725 #self.receive_list.selectedIndexes()
727 item = self.receive_list.itemAt(position)
729 addr = unicode(item.text(1))
731 menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
732 menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
733 menu.addAction(_("Edit label"), lambda: self.edit_label(True))
734 if self.wallet.gui_detailed_view:
735 t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
736 menu.addAction(t, lambda: self.toggle_freeze(addr))
737 t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
738 menu.addAction(t, lambda: self.toggle_priority(addr))
739 menu.exec_(self.receive_list.viewport().mapToGlobal(position))
742 def payto(self, x, is_alias):
749 label = self.wallet.labels.get(addr)
750 m_addr = label + ' <' + addr + '>' if label else addr
751 self.tabs.setCurrentIndex(1)
752 self.payto_e.setText(m_addr)
753 self.amount_e.setFocus()
755 def delete_contact(self, x, is_alias):
756 if self.question("Do you want to remove %s from your list of contacts?"%x):
757 if not is_alias and x in self.wallet.addressbook:
758 self.wallet.addressbook.remove(x)
759 if x in self.wallet.labels.keys():
760 self.wallet.labels.pop(x)
761 elif is_alias and x in self.wallet.aliases:
762 self.wallet.aliases.pop(x)
763 self.update_history_tab()
764 self.update_contacts_tab()
765 self.update_completions()
767 def create_contact_menu(self, position):
768 # fixme: this function apparently has a side effect.
769 # if it is not called the menu pops up several times
770 #self.contacts_list.selectedIndexes()
772 item = self.contacts_list.itemAt(position)
774 addr = unicode(item.text(0))
775 label = unicode(item.text(1))
776 is_alias = label in self.wallet.aliases.keys()
777 x = label if is_alias else addr
779 menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
780 menu.addAction(_("Pay to"), lambda: self.payto(x, is_alias))
781 menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
783 menu.addAction(_("Edit label"), lambda: self.edit_label(False))
785 menu.addAction(_("View alias details"), lambda: self.show_contact_details(label))
786 menu.addAction(_("Delete"), lambda: self.delete_contact(x,is_alias))
787 menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
790 def update_receive_tab(self):
791 l = self.receive_list
793 l.setColumnHidden(0,not self.wallet.gui_detailed_view)
794 l.setColumnHidden(3,not self.wallet.gui_detailed_view)
795 l.setColumnHidden(4,not self.wallet.gui_detailed_view)
796 l.setColumnWidth(0, 50)
797 l.setColumnWidth(1, 310)
798 l.setColumnWidth(2, 250)
799 l.setColumnWidth(3, 130)
800 l.setColumnWidth(4, 10)
804 for address in self.wallet.all_addresses():
806 if self.wallet.is_change(address) and not self.wallet.gui_detailed_view:
809 label = self.wallet.labels.get(address,'')
811 h = self.wallet.history.get(address,[])
813 if not item['is_input'] : n=n+1
817 if address in self.wallet.addresses:
819 if gap > self.wallet.gap_limit:
822 if address in self.wallet.addresses:
825 c, u = self.wallet.get_addr_balance(address)
826 balance = format_satoshis( c + u, False, self.wallet.num_zeros )
827 flags = self.wallet.get_address_flags(address)
828 item = QTreeWidgetItem( [ flags, address, label, balance, tx] )
830 item.setFont(0, QFont(MONOSPACE_FONT))
831 item.setFont(1, QFont(MONOSPACE_FONT))
832 item.setFont(3, QFont(MONOSPACE_FONT))
833 if address in self.wallet.frozen_addresses:
834 item.setBackgroundColor(1, QColor('lightblue'))
835 elif address in self.wallet.prioritized_addresses:
836 item.setBackgroundColor(1, QColor('lightgreen'))
837 if is_red and address in self.wallet.addresses:
838 item.setBackgroundColor(1, QColor('red'))
839 l.addTopLevelItem(item)
841 # we use column 1 because column 0 may be hidden
842 l.setCurrentItem(l.topLevelItem(0),1)
844 def show_contact_details(self, m):
845 a = self.wallet.aliases.get(m)
847 if a[0] in self.wallet.authorities.keys():
848 s = self.wallet.authorities.get(a[0])
851 msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
852 QMessageBox.information(self, 'Alias', msg, 'OK')
854 def update_contacts_tab(self):
856 l = self.contacts_list
858 l.setColumnHidden(2, not self.wallet.gui_detailed_view)
859 l.setColumnWidth(0, 350)
860 l.setColumnWidth(1, 330)
861 l.setColumnWidth(2, 100)
864 for alias, v in self.wallet.aliases.items():
866 alias_targets.append(target)
867 item = QTreeWidgetItem( [ target, alias, '-'] )
868 item.setBackgroundColor(0, QColor('lightgray'))
869 l.addTopLevelItem(item)
871 for address in self.wallet.addressbook:
872 if address in alias_targets: continue
873 label = self.wallet.labels.get(address,'')
875 for item in self.wallet.tx_history.values():
876 if address in item['outputs'] : n=n+1
878 item = QTreeWidgetItem( [ address, label, tx] )
879 item.setFont(0, QFont(MONOSPACE_FONT))
880 l.addTopLevelItem(item)
882 l.setCurrentItem(l.topLevelItem(0))
884 def create_wall_tab(self):
885 self.textbox = textbox = QTextEdit(self)
886 textbox.setFont(QFont(MONOSPACE_FONT))
887 textbox.setReadOnly(True)
890 def create_status_bar(self):
892 sb.setFixedHeight(35)
894 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) )
895 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) )
897 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/seed.png"), "Seed", lambda: self.show_seed_dialog(self.wallet, self) ) )
898 self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) )
899 sb.addPermanentWidget( self.status_button )
900 self.setStatusBar(sb)
902 def new_contact_dialog(self):
903 text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':')
904 address = unicode(text)
906 if self.wallet.is_valid(address):
907 self.wallet.addressbook.append(address)
909 self.update_contacts_tab()
910 self.update_history_tab()
911 self.update_completions()
913 QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
916 def show_seed_dialog(wallet, parent=None):
918 QMessageBox.information(parent, _('Message'),
919 _('No seed'), _('OK'))
922 if wallet.use_encryption:
923 password = parent.password_dialog()
930 seed = wallet.pw_decode(wallet.seed, password)
932 QMessageBox.warning(parent, _('Error'),
933 _('Incorrect Password'), _('OK'))
936 dialog = QDialog(None)
938 dialog.setWindowTitle(_("Seed"))
940 brainwallet = ' '.join(mnemonic.mn_encode(seed))
942 msg = _("Your wallet generation seed is") +":<p>\"" + brainwallet + "\"<p>" \
943 + _("Please write down or memorize these 12 words (order is important).") + " " \
944 + _("This seed will allow you to recover your wallet in case of computer failure.") + "<p>" \
945 + _("WARNING: Never disclose your seed. Never type it on a website.") + "<p>"
947 main_text = QLabel(msg)
948 main_text.setWordWrap(True)
951 logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
958 copy_function = lambda: app.clipboard().setText(brainwallet)
959 copy_button = QPushButton(_("Copy to Clipboard"))
960 copy_button.clicked.connect(copy_function)
962 show_qr_function = lambda: ElectrumWindow.show_seed_qrcode(seed)
963 qr_button = QPushButton(_("View as QR Code"))
964 qr_button.clicked.connect(show_qr_function)
966 ok_button = QPushButton(_("OK"))
967 ok_button.clicked.connect(dialog.accept)
969 main_layout = QGridLayout()
970 main_layout.addWidget(logo, 0, 0)
971 main_layout.addWidget(main_text, 0, 1, 1, -1)
972 main_layout.addWidget(copy_button, 1, 1)
973 main_layout.addWidget(qr_button, 1, 2)
974 main_layout.addWidget(ok_button, 1, 3)
975 dialog.setLayout(main_layout)
980 def show_seed_qrcode(seed):
984 d.setWindowTitle(_("Seed"))
985 d.setMinimumSize(270, 300)
987 vbox.addWidget(QRCodeWidget(seed))
990 b = QPushButton(_("OK"))
992 b.clicked.connect(d.accept)
999 def show_address_qrcode(self,address):
1000 if not address: return
1003 d.setWindowTitle(address)
1004 d.setMinimumSize(270, 350)
1005 vbox = QVBoxLayout()
1006 qrw = QRCodeWidget(address)
1009 hbox = QHBoxLayout()
1010 amount_e = QLineEdit()
1011 hbox.addWidget(QLabel(_('Amount')))
1012 hbox.addWidget(amount_e)
1013 vbox.addLayout(hbox)
1015 #hbox = QHBoxLayout()
1016 #label_e = QLineEdit()
1017 #hbox.addWidget(QLabel('Label'))
1018 #hbox.addWidget(label_e)
1019 #vbox.addLayout(hbox)
1021 def amount_changed():
1022 amount = numbify(amount_e)
1023 #label = str( label_e.getText() )
1024 if amount is not None:
1025 qrw.set_addr('bitcoin:%s?amount=%s'%(address,str( Decimal(amount) /100000000)))
1027 qrw.set_addr( address )
1031 bmp.save_qrcode(qrw.qr, "qrcode.bmp")
1032 self.show_message(_("QR code saved to file") + " 'qrcode.bmp'")
1034 amount_e.textChanged.connect( amount_changed )
1036 hbox = QHBoxLayout()
1038 b = QPushButton(_("Save"))
1039 b.clicked.connect(do_save)
1041 b = QPushButton(_("Close"))
1043 b.clicked.connect(d.accept)
1045 vbox.addLayout(hbox)
1049 def question(self, msg):
1050 return QMessageBox.question(self, _('Message'), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
1052 def show_message(self, msg):
1053 QMessageBox.information(self, _('Message'), msg, _('OK'))
1055 def password_dialog(self ):
1062 vbox = QVBoxLayout()
1063 msg = _('Please enter your password')
1064 vbox.addWidget(QLabel(msg))
1066 grid = QGridLayout()
1068 grid.addWidget(QLabel(_('Password')), 1, 0)
1069 grid.addWidget(pw, 1, 1)
1070 vbox.addLayout(grid)
1072 vbox.addLayout(ok_cancel_buttons(d))
1075 if not d.exec_(): return
1076 return unicode(pw.text())
1083 def change_password_dialog( wallet, parent=None ):
1086 QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
1094 new_pw = QLineEdit()
1095 new_pw.setEchoMode(2)
1096 conf_pw = QLineEdit()
1097 conf_pw.setEchoMode(2)
1099 vbox = QVBoxLayout()
1101 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')
1103 msg = _("Please choose a password to encrypt your wallet keys.")+'\n'+_("Leave these fields empty if you want to disable encryption.")
1104 vbox.addWidget(QLabel(msg))
1106 grid = QGridLayout()
1109 if wallet.use_encryption:
1110 grid.addWidget(QLabel(_('Password')), 1, 0)
1111 grid.addWidget(pw, 1, 1)
1113 grid.addWidget(QLabel(_('New Password')), 2, 0)
1114 grid.addWidget(new_pw, 2, 1)
1116 grid.addWidget(QLabel(_('Confirm Password')), 3, 0)
1117 grid.addWidget(conf_pw, 3, 1)
1118 vbox.addLayout(grid)
1120 vbox.addLayout(ok_cancel_buttons(d))
1123 if not d.exec_(): return
1125 password = unicode(pw.text()) if wallet.use_encryption else None
1126 new_password = unicode(new_pw.text())
1127 new_password2 = unicode(conf_pw.text())
1130 seed = wallet.pw_decode( wallet.seed, password)
1132 QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
1135 if new_password != new_password2:
1136 QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
1139 wallet.update_password(seed, password, new_password)
1142 def seed_dialog(wallet, parent=None):
1146 vbox = QVBoxLayout()
1147 msg = _("Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet.")
1148 vbox.addWidget(QLabel(msg))
1150 grid = QGridLayout()
1153 seed_e = QLineEdit()
1154 grid.addWidget(QLabel(_('Seed or mnemonic')), 1, 0)
1155 grid.addWidget(seed_e, 1, 1)
1159 grid.addWidget(QLabel(_('Gap limit')), 2, 0)
1160 grid.addWidget(gap_e, 2, 1)
1161 gap_e.textChanged.connect(lambda: numbify(gap_e,True))
1162 vbox.addLayout(grid)
1164 vbox.addLayout(ok_cancel_buttons(d))
1167 if not d.exec_(): return
1170 gap = int(unicode(gap_e.text()))
1172 QMessageBox.warning(None, _('Error'), 'error', 'OK')
1176 seed = unicode(seed_e.text())
1179 print_error("Warning: Not hex, trying decode")
1181 seed = mnemonic.mn_decode( seed.split(' ') )
1183 QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK'))
1186 QMessageBox.warning(None, _('Error'), _('No seed'), 'OK')
1189 wallet.seed = str(seed)
1190 #print repr(wallet.seed)
1191 wallet.gap_limit = gap
1196 def settings_dialog(self):
1199 vbox = QVBoxLayout()
1200 msg = _('Here are the settings of your wallet.') + '\n'\
1201 + _('For more explanations, click on the help buttons next to each field.')
1204 label.setFixedWidth(250)
1205 label.setWordWrap(True)
1206 label.setAlignment(Qt.AlignJustify)
1207 vbox.addWidget(label)
1209 grid = QGridLayout()
1211 vbox.addLayout(grid)
1214 fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) )
1215 grid.addWidget(QLabel(_('Transaction fee')), 2, 0)
1216 grid.addWidget(fee_e, 2, 1)
1217 msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \
1218 + _('Recommended value') + ': 0.001'
1219 grid.addWidget(HelpButton(msg), 2, 2)
1220 fee_e.textChanged.connect(lambda: numbify(fee_e,False))
1223 nz_e.setText("%d"% self.wallet.num_zeros)
1224 grid.addWidget(QLabel(_('Display zeros')), 3, 0)
1225 msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
1226 grid.addWidget(HelpButton(msg), 3, 2)
1227 grid.addWidget(nz_e, 3, 1)
1228 nz_e.textChanged.connect(lambda: numbify(nz_e,True))
1230 usechange_cb = QCheckBox(_('Use change addresses'))
1231 grid.addWidget(usechange_cb, 5, 0)
1232 usechange_cb.setChecked(self.wallet.use_change)
1233 grid.addWidget(HelpButton(_('Using a change addresses makes it more difficult for other people to track your transactions. ')), 5, 2)
1235 msg = _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \
1236 + _('You may increase it if you need more receiving addresses.') + '\n\n' \
1237 + _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \
1238 + _('Given the current status of your address sequence, the minimum gap limit you can use is: ') + '%d'%self.wallet.min_acceptable_gap() + '\n\n' \
1239 + _('Warning') + ': ' \
1240 + _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \
1241 + _('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'
1243 gap_e.setText("%d"% self.wallet.gap_limit)
1244 grid.addWidget(QLabel(_('Gap limit')), 6, 0)
1245 grid.addWidget(gap_e, 6, 1)
1246 grid.addWidget(HelpButton(msg), 6, 2)
1247 gap_e.textChanged.connect(lambda: numbify(nz_e,True))
1250 gui.addItems(['Lite', 'Qt'])
1251 cfg = SimpleConfig()
1252 gui.setCurrentIndex(gui.findText(cfg.config["gui"].capitalize()))
1253 grid.addWidget(QLabel(_('Default GUI') + ':'), 7, 0)
1254 grid.addWidget(gui, 7, 1)
1255 grid.addWidget(HelpButton(_('Select which GUI mode to use at start up. ')), 7, 2)
1257 vbox.addLayout(ok_cancel_buttons(d))
1261 if not d.exec_(): return
1263 fee = unicode(fee_e.text())
1265 fee = int( 100000000 * Decimal(fee) )
1267 QMessageBox.warning(self, _('Error'), _('Invalid value') +': %s'%fee, _('OK'))
1270 if self.wallet.fee != fee:
1271 self.wallet.fee = fee
1274 nz = unicode(nz_e.text())
1279 QMessageBox.warning(self, _('Error'), _('Invalid value')+':%s'%nz, _('OK'))
1282 if self.wallet.num_zeros != nz:
1283 self.wallet.num_zeros = nz
1284 self.update_history_tab()
1285 self.update_receive_tab()
1288 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.set_key("gui", str(gui.currentText()).lower())
1308 def network_dialog(wallet, parent=None):
1309 interface = wallet.interface
1311 if interface.is_connected:
1312 status = _("Connected to")+" %s:%d\n%d blocks"%(interface.host, interface.port, wallet.blocks)
1314 status = _("Not connected")
1315 server = wallet.server
1318 status = _("Please choose a server.")
1319 server = random.choice( DEFAULT_SERVERS )
1321 if not wallet.interface.servers:
1323 for x in DEFAULT_SERVERS:
1324 h,port,protocol = x.split(':')
1325 servers_list.append( (h,[(protocol,port)] ) )
1327 servers_list = wallet.interface.servers
1330 for item in servers_list:
1334 protocol, port = item2
1340 d.setWindowTitle(_('Server'))
1341 d.setMinimumSize(375, 20)
1343 vbox = QVBoxLayout()
1346 hbox = QHBoxLayout()
1348 l.setPixmap(QPixmap(":icons/network.png"))
1350 hbox.addWidget(QLabel(status))
1352 vbox.addLayout(hbox)
1354 hbox = QHBoxLayout()
1355 host_line = QLineEdit()
1356 host_line.setText(server)
1357 hbox.addWidget(QLabel(_('Connect to') + ':'))
1358 hbox.addWidget(host_line)
1359 vbox.addLayout(hbox)
1361 hbox = QHBoxLayout()
1363 buttonGroup = QGroupBox(_("Protocol"))
1364 radio1 = QRadioButton("tcp", buttonGroup)
1365 radio2 = QRadioButton("http", buttonGroup)
1368 return unicode(host_line.text()).split(':')
1370 def set_button(protocol):
1372 radio1.setChecked(1)
1373 elif protocol == 'h':
1374 radio2.setChecked(1)
1376 def set_protocol(protocol):
1377 host = current_line()[0]
1379 if protocol not in pp.keys():
1380 protocol = pp.keys()[0]
1381 set_button(protocol)
1383 host_line.setText( host + ':' + port + ':' + protocol)
1385 radio1.clicked.connect(lambda x: set_protocol('t') )
1386 radio2.clicked.connect(lambda x: set_protocol('h') )
1388 set_button(current_line()[2])
1390 hbox.addWidget(QLabel(_('Protocol')+':'))
1391 hbox.addWidget(radio1)
1392 hbox.addWidget(radio2)
1394 vbox.addLayout(hbox)
1396 hbox = QHBoxLayout()
1397 proxy_mode = QComboBox()
1398 proxy_host = QLineEdit()
1399 proxy_host.setFixedWidth(200)
1400 proxy_port = QLineEdit()
1401 proxy_port.setFixedWidth(50)
1402 proxy_mode.addItems(['NONE', 'SOCKS4', 'SOCKS5', 'HTTP'])
1404 def check_for_disable(index = False):
1405 if proxy_mode.currentText() != 'NONE':
1406 proxy_host.setEnabled(True)
1407 proxy_port.setEnabled(True)
1409 proxy_host.setEnabled(False)
1410 proxy_port.setEnabled(False)
1414 proxy_mode.connect(proxy_mode, SIGNAL('currentIndexChanged(int)'), check_for_disable)
1417 proxy_config = interface.proxy if interface.proxy else { "mode":"none", "host":"localhost", "port":"8080"}
1418 proxy_mode.setCurrentIndex(proxy_mode.findText(str(proxy_config.get("mode").upper())))
1419 proxy_host.setText(proxy_config.get("host"))
1420 proxy_port.setText(proxy_config.get("port"))
1422 hbox.addWidget(QLabel(_('Proxy') + ':'))
1423 hbox.addWidget(proxy_mode)
1424 hbox.addWidget(proxy_host)
1425 hbox.addWidget(proxy_port)
1426 vbox.addLayout(hbox)
1428 hbox = QHBoxLayout()
1430 if wallet.interface.servers:
1431 label = _('Active Servers')
1433 label = _('Default Servers')
1435 servers_list_widget = QTreeWidget(parent)
1436 servers_list_widget.setHeaderLabels( [ label ] )
1437 servers_list_widget.setMaximumHeight(150)
1438 for host in plist.keys():
1439 servers_list_widget.addTopLevelItem(QTreeWidgetItem( [ host ] ))
1442 host = unicode(x.text(0))
1444 if 't' in pp.keys():
1447 protocol = pp.keys()[0]
1449 host_line.setText( host + ':' + port + ':' + protocol)
1450 set_button(protocol)
1452 servers_list_widget.connect(servers_list_widget, SIGNAL('itemClicked(QTreeWidgetItem*, int)'), do_set_line)
1453 vbox.addWidget(servers_list_widget)
1455 vbox.addLayout(ok_cancel_buttons(d))
1458 if not d.exec_(): return
1459 server = unicode( host_line.text() )
1462 if proxy_mode.currentText() != 'NONE':
1463 proxy = { u'mode':unicode(proxy_mode.currentText()).lower(), u'host':unicode(proxy_host.text()), u'port':unicode(proxy_port.text()) }
1467 cfg = SimpleConfig()
1468 cfg.set_key("proxy", proxy, True)
1469 wallet.set_server(server, proxy)
1471 except Exception as err:
1472 QMessageBox.information(None, _('Error'), str(err), _('OK'))
1480 def closeEvent(self, event):
1481 cfg = SimpleConfig()
1483 cfg.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()])
1490 def __init__(self, wallet, app=None):
1491 self.wallet = wallet
1493 self.app = QApplication(sys.argv)
1495 def server_list_changed(self):
1498 def waiting_dialog(self):
1504 w.setWindowTitle('Electrum')
1506 vbox = QVBoxLayout()
1511 if self.wallet.up_to_date:
1514 l.setText("Please wait...\nAddresses generated: %d\nKilobytes received: %.1f"\
1515 %(len(self.wallet.all_addresses()), self.wallet.interface.bytes_received/1024.))
1517 w.connect(s, QtCore.SIGNAL('timersignal'), f)
1518 self.wallet.interface.poke()
1523 def restore_or_create(self):
1525 msg = _("Wallet file not found.")+"\n"+_("Do you want to create a new wallet, or to restore an existing one?")
1526 r = QMessageBox.question(None, _('Message'), msg, _('Create'), _('Restore'), _('Cancel'), 0, 2)
1527 if r==2: return False
1529 is_recovery = (r==1)
1530 wallet = self.wallet
1531 # ask for the server.
1532 if not ElectrumWindow.network_dialog( wallet, parent=None ): return False
1535 wallet.new_seed(None)
1536 wallet.init_mpk( wallet.seed )
1537 wallet.up_to_date_event.clear()
1538 wallet.up_to_date = False
1539 self.waiting_dialog()
1540 # run a dialog indicating the seed, ask the user to remember it
1541 ElectrumWindow.show_seed_dialog(wallet)
1543 ElectrumWindow.change_password_dialog(wallet)
1545 # ask for seed and gap.
1546 if not ElectrumWindow.seed_dialog( wallet ): return False
1547 wallet.init_mpk( wallet.seed )
1548 wallet.up_to_date_event.clear()
1549 wallet.up_to_date = False
1550 self.waiting_dialog()
1551 if wallet.is_found():
1552 # history and addressbook
1553 wallet.update_tx_history()
1554 wallet.fill_addressbook()
1555 print "Recovery successful"
1558 QMessageBox.information(None, _('Error'), _("No transactions found for this seed"), _('OK'))
1566 w = ElectrumWindow(self.wallet)
1567 if url: w.set_url(url)