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 import bmp, mnemonic, pyqrnative, qrscanner
42 from decimal import Decimal
46 if platform.system() == 'Windows':
47 MONOSPACE_FONT = 'Lucida Console'
48 elif platform.system() == 'Darwin':
49 MONOSPACE_FONT = 'Monaco'
51 MONOSPACE_FONT = 'monospace'
53 ALIAS_REGEXP = '^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$'
55 def numbify(entry, is_int = False):
56 text = unicode(entry.text()).strip()
57 pos = entry.cursorPosition()
59 if not is_int: chars +='.'
60 s = ''.join([i for i in text if i in chars])
65 s = s[:p] + '.' + s[p:p+8]
67 amount = int( Decimal(s) * 100000000 )
76 entry.setCursorPosition(pos)
80 class Timer(QtCore.QThread):
83 self.emit(QtCore.SIGNAL('timersignal'))
86 class HelpButton(QPushButton):
87 def __init__(self, text):
88 QPushButton.__init__(self, '?')
89 self.setFocusPolicy(Qt.NoFocus)
90 self.setFixedWidth(20)
91 self.clicked.connect(lambda: QMessageBox.information(self, 'Help', text, 'OK') )
94 class EnterButton(QPushButton):
95 def __init__(self, text, func):
96 QPushButton.__init__(self, text)
98 self.clicked.connect(func)
100 def keyPressEvent(self, e):
101 if e.key() == QtCore.Qt.Key_Return:
104 class MyTreeWidget(QTreeWidget):
105 def __init__(self, parent):
106 QTreeWidget.__init__(self, parent)
109 for i in range(0,self.viewport().height()/5):
110 if self.itemAt(QPoint(0,i*5)) == item:
114 for j in range(0,30):
115 if self.itemAt(QPoint(0,i*5 + j)) != item:
117 self.emit(SIGNAL('customContextMenuRequested(const QPoint&)'), QPoint(50, i*5 + j - 1))
119 self.connect(self, SIGNAL('itemActivated(QTreeWidgetItem*, int)'), ddfr)
124 class StatusBarButton(QPushButton):
125 def __init__(self, icon, tooltip, func):
126 QPushButton.__init__(self, icon, '')
127 self.setToolTip(tooltip)
129 self.setMaximumWidth(25)
130 self.clicked.connect(func)
133 def keyPressEvent(self, e):
134 if e.key() == QtCore.Qt.Key_Return:
138 class QRCodeWidget(QWidget):
140 def __init__(self, addr):
141 super(QRCodeWidget, self).__init__()
142 self.setGeometry(300, 300, 350, 350)
145 def set_addr(self, addr):
147 self.qr = pyqrnative.QRCode(4, pyqrnative.QRErrorCorrectLevel.L)
148 self.qr.addData(addr)
151 def paintEvent(self, e):
152 qp = QtGui.QPainter()
155 size = self.qr.getModuleCount()*boxsize
156 k = self.qr.getModuleCount()
157 black = QColor(0, 0, 0, 255)
158 white = QColor(255, 255, 255, 255)
161 if self.qr.isDark(r, c):
167 qp.drawRect(c*boxsize, r*boxsize, boxsize, boxsize)
172 def ok_cancel_buttons(dialog):
175 b = QPushButton("OK")
177 b.clicked.connect(dialog.accept)
178 b = QPushButton("Cancel")
180 b.clicked.connect(dialog.reject)
184 class ElectrumWindow(QMainWindow):
186 def __init__(self, wallet, config):
187 QMainWindow.__init__(self)
190 self.wallet.register_callback(self.update_callback)
192 self.detailed_view = config.get('qt_detailed_view', False)
194 self.funds_error = False
195 self.completions = QStringListModel()
197 self.tabs = tabs = QTabWidget(self)
198 tabs.addTab(self.create_history_tab(), _('History') )
200 tabs.addTab(self.create_send_tab(), _('Send') )
201 tabs.addTab(self.create_receive_tab(), _('Receive') )
202 tabs.addTab(self.create_contacts_tab(), _('Contacts') )
203 tabs.addTab(self.create_wall_tab(), _('Wall') )
204 tabs.setMinimumSize(600, 400)
205 tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
206 self.setCentralWidget(tabs)
207 self.create_status_bar()
209 g = self.config.get("winpos-qt",[100, 100, 840, 400])
210 self.setGeometry(g[0], g[1], g[2], g[3])
211 title = 'Electrum ' + self.wallet.electrum_version + ' - ' + self.config.path
212 if not self.wallet.seed: title += ' [seedless]'
213 self.setWindowTitle( title )
215 QShortcut(QKeySequence("Ctrl+W"), self, self.close)
216 QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
217 QShortcut(QKeySequence("Ctrl+PgUp"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() - 1 )%tabs.count() ))
218 QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() + 1 )%tabs.count() ))
220 self.connect(self, QtCore.SIGNAL('updatesignal'), self.update_wallet)
221 self.history_list.setFocus(True)
223 # dark magic fix by flatfly; https://bitcointalk.org/index.php?topic=73651.msg959913#msg959913
224 if platform.system() == 'Windows':
225 n = 3 if self.wallet.seed else 2
226 tabs.setCurrentIndex (n)
227 tabs.setCurrentIndex (0)
230 def connect_slots(self, sender):
232 self.connect(sender, QtCore.SIGNAL('timersignal'), self.check_recipient)
233 self.previous_payto_e=''
235 def check_recipient(self):
236 if self.payto_e.hasFocus():
238 r = unicode( self.payto_e.text() )
239 if r != self.previous_payto_e:
240 self.previous_payto_e = r
242 if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
244 to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
248 s = r + ' <' + to_address + '>'
249 self.payto_e.setText(s)
252 def update_callback(self):
253 self.emit(QtCore.SIGNAL('updatesignal'))
255 def update_wallet(self):
256 if self.wallet.interface and self.wallet.interface.is_connected:
257 if self.wallet.blocks == -1:
258 text = _( "Connecting..." )
259 icon = QIcon(":icons/status_disconnected.png")
260 elif self.wallet.blocks == 0:
261 text = _( "Server not ready" )
262 icon = QIcon(":icons/status_disconnected.png")
263 elif not self.wallet.up_to_date:
264 text = _( "Synchronizing..." )
265 icon = QIcon(":icons/status_waiting.png")
267 c, u = self.wallet.get_balance()
268 text = _( "Balance" ) + ": %s "%( format_satoshis(c,False,self.wallet.num_zeros) )
269 if u: text += "[%s unconfirmed]"%( format_satoshis(u,True,self.wallet.num_zeros).strip() )
270 icon = QIcon(":icons/status_connected.png")
272 text = _( "Not connected" )
273 icon = QIcon(":icons/status_disconnected.png")
276 text = _( "Not enough funds" )
278 self.statusBar().showMessage(text)
279 self.status_button.setIcon( icon )
281 if self.wallet.up_to_date:
282 self.textbox.setText( self.wallet.banner )
283 self.update_history_tab()
284 self.update_receive_tab()
285 self.update_contacts_tab()
286 self.update_completions()
289 def create_history_tab(self):
290 self.history_list = l = MyTreeWidget(self)
292 l.setColumnWidth(0, 40)
293 l.setColumnWidth(1, 140)
294 l.setColumnWidth(2, 350)
295 l.setColumnWidth(3, 140)
296 l.setColumnWidth(4, 140)
297 l.setHeaderLabels( [ '', _( 'Date' ), _( 'To / From' ) , _('Amount'), _('Balance')] )
298 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.tx_label_clicked)
299 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), self.tx_label_changed)
300 l.setContextMenuPolicy(Qt.CustomContextMenu)
301 l.customContextMenuRequested.connect(self.create_history_menu)
304 def create_history_menu(self, position):
305 self.history_list.selectedIndexes()
306 item = self.history_list.currentItem()
308 tx_hash = str(item.toolTip(0))
310 menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash))
311 menu.addAction(_("Details"), lambda: self.tx_details(tx_hash))
312 menu.addAction(_("Edit description"), lambda: self.tx_label_clicked(item,2))
313 menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
315 def tx_details(self, tx_hash):
316 tx = self.wallet.tx_history.get(tx_hash)
319 conf = self.wallet.blocks - tx['height'] + 1
320 time_str = datetime.datetime.fromtimestamp( tx['timestamp']).isoformat(' ')[:-3]
325 tx_details = _("Transaction Details") +"\n\n" \
326 + "Transaction ID:\n" + tx_hash + "\n\n" \
327 + "Status: %d confirmations\n\n"%conf \
328 + "Date: %s\n\n"%time_str \
329 + "Inputs:\n-"+ '\n-'.join(tx['inputs']) + "\n\n" \
330 + "Outputs:\n-"+ '\n-'.join(tx['outputs'])
332 r = self.wallet.receipts.get(tx_hash)
334 tx_details += "\n_______________________________________" \
335 + '\n\nSigned URI: ' + r[2] \
336 + "\n\nSigned by: " + r[0] \
337 + '\n\nSignature: ' + r[1]
339 QMessageBox.information(self, 'Details', tx_details, 'OK')
342 def tx_label_clicked(self, item, column):
343 if column==2 and item.isSelected():
344 tx_hash = str(item.toolTip(0))
346 #if not self.wallet.labels.get(tx_hash): item.setText(2,'')
347 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
348 self.history_list.editItem( item, column )
349 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
352 def tx_label_changed(self, item, column):
356 tx_hash = str(item.toolTip(0))
357 tx = self.wallet.tx_history.get(tx_hash)
358 s = self.wallet.labels.get(tx_hash)
359 text = unicode( item.text(2) )
361 self.wallet.labels[tx_hash] = text
362 item.setForeground(2, QBrush(QColor('black')))
364 if s: self.wallet.labels.pop(tx_hash)
365 text = tx['default_label']
366 item.setText(2, text)
367 item.setForeground(2, QBrush(QColor('gray')))
370 def edit_label(self, is_recv):
371 l = self.receive_list if is_recv else self.contacts_list
372 c = 2 if is_recv else 1
373 item = l.currentItem()
374 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
375 l.editItem( item, c )
376 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
378 def address_label_clicked(self, item, column, l, column_addr, column_label):
379 if column==column_label and item.isSelected():
380 addr = unicode( item.text(column_addr) )
381 label = unicode( item.text(column_label) )
382 if label in self.wallet.aliases.keys():
384 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
385 l.editItem( item, column )
386 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
388 def address_label_changed(self, item, column, l, column_addr, column_label):
389 addr = unicode( item.text(column_addr) )
390 text = unicode( item.text(column_label) )
392 if text not in self.wallet.aliases.keys():
393 self.wallet.labels[addr] = text
395 print_error("Error: This is one of your aliases")
396 label = self.wallet.labels.get(addr,'')
397 item.setText(column_label, QString(label))
399 s = self.wallet.labels.get(addr)
400 if s: self.wallet.labels.pop(addr)
402 self.update_history_tab()
403 self.update_completions()
405 def update_history_tab(self):
406 self.history_list.clear()
408 for tx in self.wallet.get_tx_history():
409 tx_hash = tx['tx_hash']
411 conf = self.wallet.blocks - tx['height'] + 1
412 time_str = datetime.datetime.fromtimestamp( tx['timestamp']).isoformat(' ')[:-3]
413 icon = QIcon(":icons/confirmed.png")
417 icon = QIcon(":icons/unconfirmed.png")
420 label = self.wallet.labels.get(tx_hash)
421 is_default_label = (label == '') or (label is None)
422 if is_default_label: label = tx['default_label']
424 item = QTreeWidgetItem( [ '', time_str, label, format_satoshis(v,True,self.wallet.num_zeros), format_satoshis(balance,False,self.wallet.num_zeros)] )
425 item.setFont(2, QFont(MONOSPACE_FONT))
426 item.setFont(3, QFont(MONOSPACE_FONT))
427 item.setFont(4, QFont(MONOSPACE_FONT))
428 item.setToolTip(0, tx_hash)
430 item.setForeground(2, QBrush(QColor('grey')))
432 item.setIcon(0, icon)
433 self.history_list.insertTopLevelItem(0,item)
435 self.history_list.setCurrentItem(self.history_list.topLevelItem(0))
438 def create_send_tab(self):
443 grid.setColumnMinimumWidth(3,300)
444 grid.setColumnStretch(5,1)
446 self.payto_e = QLineEdit()
447 grid.addWidget(QLabel(_('Pay to')), 1, 0)
448 grid.addWidget(self.payto_e, 1, 1, 1, 3)
451 qrcode = qrscanner.scan_qr()
452 if 'address' in qrcode:
453 self.payto_e.setText(qrcode['address'])
454 if 'amount' in qrcode:
455 self.amount_e.setText(str(qrcode['amount']))
456 if 'label' in qrcode:
457 self.message_e.setText(qrcode['label'])
458 if 'message' in qrcode:
459 self.message_e.setText("%s (%s)" % (self.message_e.text(), qrcode['message']))
462 if qrscanner.is_available():
463 b = QPushButton(_("Scan QR code"))
464 b.clicked.connect(fill_from_qr)
465 grid.addWidget(b, 1, 5)
467 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)
469 completer = QCompleter()
470 completer.setCaseSensitivity(False)
471 self.payto_e.setCompleter(completer)
472 completer.setModel(self.completions)
474 self.message_e = QLineEdit()
475 grid.addWidget(QLabel(_('Description')), 2, 0)
476 grid.addWidget(self.message_e, 2, 1, 1, 3)
477 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)
479 self.amount_e = QLineEdit()
480 grid.addWidget(QLabel(_('Amount')), 3, 0)
481 grid.addWidget(self.amount_e, 3, 1, 1, 2)
482 grid.addWidget(HelpButton(
483 _('Amount to be sent.') + '\n\n' \
484 + _('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)
486 self.fee_e = QLineEdit()
487 grid.addWidget(QLabel(_('Fee')), 4, 0)
488 grid.addWidget(self.fee_e, 4, 1, 1, 2)
489 grid.addWidget(HelpButton(
490 _('Bitcoin transactions are in general not free. A transaction fee is paid by the sender of the funds.') + '\n\n'\
491 + _('The amount of fee can be decided freely by the sender. However, transactions with low fees take more time to be processed.') + '\n\n'\
492 + _('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)
494 b = EnterButton(_("Send"), self.do_send)
495 grid.addWidget(b, 6, 1)
497 b = EnterButton(_("Clear"),self.do_clear)
498 grid.addWidget(b, 6, 2)
500 self.payto_sig = QLabel('')
501 grid.addWidget(self.payto_sig, 7, 0, 1, 4)
503 QShortcut(QKeySequence("Up"), w, w.focusPreviousChild)
504 QShortcut(QKeySequence("Down"), w, w.focusNextChild)
513 def entry_changed( is_fee ):
514 self.funds_error = False
515 amount = numbify(self.amount_e)
516 fee = numbify(self.fee_e)
517 if not is_fee: fee = None
520 inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee )
522 self.fee_e.setText( str( Decimal( fee ) / 100000000 ) )
525 palette.setColor(self.amount_e.foregroundRole(), QColor('black'))
528 palette.setColor(self.amount_e.foregroundRole(), QColor('red'))
529 self.funds_error = True
530 self.amount_e.setPalette(palette)
531 self.fee_e.setPalette(palette)
533 self.amount_e.textChanged.connect(lambda: entry_changed(False) )
534 self.fee_e.textChanged.connect(lambda: entry_changed(True) )
539 def update_completions(self):
541 for addr,label in self.wallet.labels.items():
542 if addr in self.wallet.addressbook:
543 l.append( label + ' <' + addr + '>')
544 l = l + self.wallet.aliases.keys()
546 self.completions.setStringList(l)
552 label = unicode( self.message_e.text() )
553 r = unicode( self.payto_e.text() )
557 m1 = re.match(ALIAS_REGEXP, r)
558 # label or alias, with address in brackets
559 m2 = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
562 to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
566 to_address = m2.group(2)
570 if not self.wallet.is_valid(to_address):
571 QMessageBox.warning(self, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK'))
575 amount = int( Decimal( unicode( self.amount_e.text())) * 100000000 )
577 QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
580 fee = int( Decimal( unicode( self.fee_e.text())) * 100000000 )
582 QMessageBox.warning(self, _('Error'), _('Invalid Fee'), _('OK'))
585 if self.wallet.use_encryption:
586 password = self.password_dialog()
593 tx = self.wallet.mktx( to_address, amount, label, password, fee)
594 except BaseException, e:
595 self.show_message(str(e))
598 status, msg = self.wallet.sendtx( tx )
600 QMessageBox.information(self, '', _('Payment sent.')+'\n'+msg, _('OK'))
602 self.update_contacts_tab()
604 QMessageBox.warning(self, _('Error'), msg, _('OK'))
607 def set_url(self, url):
608 payto, amount, label, message, signature, identity, url = self.wallet.parse_url(url, self.show_message, self.question)
609 self.tabs.setCurrentIndex(1)
610 label = self.wallet.labels.get(payto)
611 m_addr = label + ' <'+ payto+'>' if label else payto
612 self.payto_e.setText(m_addr)
614 self.message_e.setText(message)
615 self.amount_e.setText(amount)
617 self.set_frozen(self.payto_e,True)
618 self.set_frozen(self.amount_e,True)
619 self.set_frozen(self.message_e,True)
620 self.payto_sig.setText( ' The bitcoin URI was signed by ' + identity )
622 self.payto_sig.setVisible(False)
625 self.payto_sig.setVisible(False)
626 for e in [self.payto_e, self.message_e, self.amount_e, self.fee_e]:
628 self.set_frozen(e,False)
630 def set_frozen(self,entry,frozen):
632 entry.setReadOnly(True)
633 entry.setFrame(False)
635 palette.setColor(entry.backgroundRole(), QColor('lightgray'))
636 entry.setPalette(palette)
638 entry.setReadOnly(False)
641 palette.setColor(entry.backgroundRole(), QColor('white'))
642 entry.setPalette(palette)
645 def toggle_freeze(self,addr):
647 if addr in self.wallet.frozen_addresses:
648 self.wallet.unfreeze(addr)
650 self.wallet.freeze(addr)
651 self.update_receive_tab()
653 def toggle_priority(self,addr):
655 if addr in self.wallet.prioritized_addresses:
656 self.wallet.unprioritize(addr)
658 self.wallet.prioritize(addr)
659 self.update_receive_tab()
662 def create_list_tab(self, headers):
663 "generic tab creation method"
664 l = MyTreeWidget(self)
665 l.setColumnCount( len(headers) )
666 l.setHeaderLabels( headers )
676 vbox.addWidget(buttons)
681 buttons.setLayout(hbox)
686 def create_receive_tab(self):
687 l,w,hbox = self.create_list_tab([_('Flags'), _('Address'), _('Label'), _('Balance'), _('Tx')])
688 l.setContextMenuPolicy(Qt.CustomContextMenu)
689 l.customContextMenuRequested.connect(self.create_receive_menu)
690 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,1,2))
691 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,1,2))
692 self.receive_list = l
693 self.receive_buttons_hbox = hbox
694 self.details_button = EnterButton(self.details_button_text(), self.toggle_detailed_view)
695 hbox.addWidget(self.details_button)
699 def details_button_text(self):
700 return _('Hide details') if self.detailed_view else _('Show details')
702 def toggle_detailed_view(self):
703 self.detailed_view = not self.detailed_view
704 self.config.set_key('qt_detailed_view', self.detailed_view, True)
706 self.details_button.setText(self.details_button_text())
708 self.update_receive_tab()
709 self.update_contacts_tab()
712 def create_contacts_tab(self):
713 l,w,hbox = self.create_list_tab([_('Address'), _('Label'), _('Tx')])
714 l.setContextMenuPolicy(Qt.CustomContextMenu)
715 l.customContextMenuRequested.connect(self.create_contact_menu)
716 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,0,1))
717 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,0,1))
718 self.contacts_list = l
719 self.contacts_buttons_hbox = hbox
720 hbox.addWidget(EnterButton(_("New"), self.new_contact_dialog))
725 def create_receive_menu(self, position):
726 # fixme: this function apparently has a side effect.
727 # if it is not called the menu pops up several times
728 #self.receive_list.selectedIndexes()
730 item = self.receive_list.itemAt(position)
732 addr = unicode(item.text(1))
734 menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
735 menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
736 menu.addAction(_("Edit label"), lambda: self.edit_label(True))
738 t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
739 menu.addAction(t, lambda: self.toggle_freeze(addr))
740 t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
741 menu.addAction(t, lambda: self.toggle_priority(addr))
742 menu.exec_(self.receive_list.viewport().mapToGlobal(position))
745 def payto(self, x, is_alias):
752 label = self.wallet.labels.get(addr)
753 m_addr = label + ' <' + addr + '>' if label else addr
754 self.tabs.setCurrentIndex(1)
755 self.payto_e.setText(m_addr)
756 self.amount_e.setFocus()
758 def delete_contact(self, x, is_alias):
759 if self.question("Do you want to remove %s from your list of contacts?"%x):
760 if not is_alias and x in self.wallet.addressbook:
761 self.wallet.addressbook.remove(x)
762 if x in self.wallet.labels.keys():
763 self.wallet.labels.pop(x)
764 elif is_alias and x in self.wallet.aliases:
765 self.wallet.aliases.pop(x)
766 self.update_history_tab()
767 self.update_contacts_tab()
768 self.update_completions()
770 def create_contact_menu(self, position):
771 # fixme: this function apparently has a side effect.
772 # if it is not called the menu pops up several times
773 #self.contacts_list.selectedIndexes()
775 item = self.contacts_list.itemAt(position)
777 addr = unicode(item.text(0))
778 label = unicode(item.text(1))
779 is_alias = label in self.wallet.aliases.keys()
780 x = label if is_alias else addr
782 menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
783 menu.addAction(_("Pay to"), lambda: self.payto(x, is_alias))
784 menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
786 menu.addAction(_("Edit label"), lambda: self.edit_label(False))
788 menu.addAction(_("View alias details"), lambda: self.show_contact_details(label))
789 menu.addAction(_("Delete"), lambda: self.delete_contact(x,is_alias))
790 menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
793 def update_receive_tab(self):
794 l = self.receive_list
796 l.setColumnHidden(0,not self.detailed_view)
797 l.setColumnHidden(3,not self.detailed_view)
798 l.setColumnHidden(4,not self.detailed_view)
799 l.setColumnWidth(0, 50)
800 l.setColumnWidth(1, 310)
801 l.setColumnWidth(2, 250)
802 l.setColumnWidth(3, 130)
803 l.setColumnWidth(4, 10)
807 for address in self.wallet.all_addresses():
809 if self.wallet.is_change(address) and not self.detailed_view:
812 label = self.wallet.labels.get(address,'')
814 h = self.wallet.history.get(address,[])
816 if not item['is_input'] : n=n+1
820 if address in self.wallet.addresses:
822 if gap > self.wallet.gap_limit:
825 if address in self.wallet.addresses:
828 c, u = self.wallet.get_addr_balance(address)
829 balance = format_satoshis( c + u, False, self.wallet.num_zeros )
830 flags = self.wallet.get_address_flags(address)
831 item = QTreeWidgetItem( [ flags, address, label, balance, tx] )
833 item.setFont(0, QFont(MONOSPACE_FONT))
834 item.setFont(1, QFont(MONOSPACE_FONT))
835 item.setFont(3, QFont(MONOSPACE_FONT))
836 if address in self.wallet.frozen_addresses:
837 item.setBackgroundColor(1, QColor('lightblue'))
838 elif address in self.wallet.prioritized_addresses:
839 item.setBackgroundColor(1, QColor('lightgreen'))
840 if is_red and address in self.wallet.addresses:
841 item.setBackgroundColor(1, QColor('red'))
842 l.addTopLevelItem(item)
844 # we use column 1 because column 0 may be hidden
845 l.setCurrentItem(l.topLevelItem(0),1)
847 def show_contact_details(self, m):
848 a = self.wallet.aliases.get(m)
850 if a[0] in self.wallet.authorities.keys():
851 s = self.wallet.authorities.get(a[0])
854 msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
855 QMessageBox.information(self, 'Alias', msg, 'OK')
857 def update_contacts_tab(self):
859 l = self.contacts_list
861 l.setColumnHidden(2, not self.detailed_view)
862 l.setColumnWidth(0, 350)
863 l.setColumnWidth(1, 330)
864 l.setColumnWidth(2, 100)
867 for alias, v in self.wallet.aliases.items():
869 alias_targets.append(target)
870 item = QTreeWidgetItem( [ target, alias, '-'] )
871 item.setBackgroundColor(0, QColor('lightgray'))
872 l.addTopLevelItem(item)
874 for address in self.wallet.addressbook:
875 if address in alias_targets: continue
876 label = self.wallet.labels.get(address,'')
878 for item in self.wallet.tx_history.values():
879 if address in item['outputs'] : n=n+1
881 item = QTreeWidgetItem( [ address, label, tx] )
882 item.setFont(0, QFont(MONOSPACE_FONT))
883 l.addTopLevelItem(item)
885 l.setCurrentItem(l.topLevelItem(0))
887 def create_wall_tab(self):
888 self.textbox = textbox = QTextEdit(self)
889 textbox.setFont(QFont(MONOSPACE_FONT))
890 textbox.setReadOnly(True)
893 def create_status_bar(self):
895 sb.setFixedHeight(35)
897 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) )
898 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) )
900 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/seed.png"), "Seed", lambda: self.show_seed_dialog(self.wallet, self) ) )
901 self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) )
902 sb.addPermanentWidget( self.status_button )
903 self.setStatusBar(sb)
905 def new_contact_dialog(self):
906 text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':')
907 address = unicode(text)
909 if self.wallet.is_valid(address):
910 self.wallet.addressbook.append(address)
912 self.update_contacts_tab()
913 self.update_history_tab()
914 self.update_completions()
916 QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
919 def show_seed_dialog(wallet, parent=None):
921 QMessageBox.information(parent, _('Message'),
922 _('No seed'), _('OK'))
925 if wallet.use_encryption:
926 password = parent.password_dialog()
933 seed = wallet.pw_decode(wallet.seed, password)
935 QMessageBox.warning(parent, _('Error'),
936 _('Incorrect Password'), _('OK'))
939 dialog = QDialog(None)
941 dialog.setWindowTitle(_("Seed"))
943 brainwallet = ' '.join(mnemonic.mn_encode(seed))
945 msg = _("Your wallet generation seed is") +":<p>\"" + brainwallet + "\"<p>" \
946 + _("Please write down or memorize these 12 words (order is important).") + " " \
947 + _("This seed will allow you to recover your wallet in case of computer failure.") + "<p>" \
948 + _("WARNING: Never disclose your seed. Never type it on a website.") + "<p>"
950 main_text = QLabel(msg)
951 main_text.setWordWrap(True)
954 logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
961 copy_function = lambda: app.clipboard().setText(brainwallet)
962 copy_button = QPushButton(_("Copy to Clipboard"))
963 copy_button.clicked.connect(copy_function)
965 show_qr_function = lambda: ElectrumWindow.show_seed_qrcode(seed)
966 qr_button = QPushButton(_("View as QR Code"))
967 qr_button.clicked.connect(show_qr_function)
969 ok_button = QPushButton(_("OK"))
970 ok_button.clicked.connect(dialog.accept)
972 main_layout = QGridLayout()
973 main_layout.addWidget(logo, 0, 0)
974 main_layout.addWidget(main_text, 0, 1, 1, -1)
975 main_layout.addWidget(copy_button, 1, 1)
976 main_layout.addWidget(qr_button, 1, 2)
977 main_layout.addWidget(ok_button, 1, 3)
978 dialog.setLayout(main_layout)
983 def show_seed_qrcode(seed):
987 d.setWindowTitle(_("Seed"))
988 d.setMinimumSize(270, 300)
990 vbox.addWidget(QRCodeWidget(seed))
993 b = QPushButton(_("OK"))
995 b.clicked.connect(d.accept)
1002 def show_address_qrcode(self,address):
1003 if not address: return
1006 d.setWindowTitle(address)
1007 d.setMinimumSize(270, 350)
1008 vbox = QVBoxLayout()
1009 qrw = QRCodeWidget(address)
1012 hbox = QHBoxLayout()
1013 amount_e = QLineEdit()
1014 hbox.addWidget(QLabel(_('Amount')))
1015 hbox.addWidget(amount_e)
1016 vbox.addLayout(hbox)
1018 #hbox = QHBoxLayout()
1019 #label_e = QLineEdit()
1020 #hbox.addWidget(QLabel('Label'))
1021 #hbox.addWidget(label_e)
1022 #vbox.addLayout(hbox)
1024 def amount_changed():
1025 amount = numbify(amount_e)
1026 #label = str( label_e.getText() )
1027 if amount is not None:
1028 qrw.set_addr('bitcoin:%s?amount=%s'%(address,str( Decimal(amount) /100000000)))
1030 qrw.set_addr( address )
1034 bmp.save_qrcode(qrw.qr, "qrcode.bmp")
1035 self.show_message(_("QR code saved to file") + " 'qrcode.bmp'")
1037 amount_e.textChanged.connect( amount_changed )
1039 hbox = QHBoxLayout()
1041 b = QPushButton(_("Save"))
1042 b.clicked.connect(do_save)
1044 b = QPushButton(_("Close"))
1046 b.clicked.connect(d.accept)
1048 vbox.addLayout(hbox)
1052 def question(self, msg):
1053 return QMessageBox.question(self, _('Message'), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
1055 def show_message(self, msg):
1056 QMessageBox.information(self, _('Message'), msg, _('OK'))
1058 def password_dialog(self ):
1065 vbox = QVBoxLayout()
1066 msg = _('Please enter your password')
1067 vbox.addWidget(QLabel(msg))
1069 grid = QGridLayout()
1071 grid.addWidget(QLabel(_('Password')), 1, 0)
1072 grid.addWidget(pw, 1, 1)
1073 vbox.addLayout(grid)
1075 vbox.addLayout(ok_cancel_buttons(d))
1078 if not d.exec_(): return
1079 return unicode(pw.text())
1086 def change_password_dialog( wallet, parent=None ):
1089 QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
1097 new_pw = QLineEdit()
1098 new_pw.setEchoMode(2)
1099 conf_pw = QLineEdit()
1100 conf_pw.setEchoMode(2)
1102 vbox = QVBoxLayout()
1104 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')
1106 msg = _("Please choose a password to encrypt your wallet keys.")+'\n'+_("Leave these fields empty if you want to disable encryption.")
1107 vbox.addWidget(QLabel(msg))
1109 grid = QGridLayout()
1112 if wallet.use_encryption:
1113 grid.addWidget(QLabel(_('Password')), 1, 0)
1114 grid.addWidget(pw, 1, 1)
1116 grid.addWidget(QLabel(_('New Password')), 2, 0)
1117 grid.addWidget(new_pw, 2, 1)
1119 grid.addWidget(QLabel(_('Confirm Password')), 3, 0)
1120 grid.addWidget(conf_pw, 3, 1)
1121 vbox.addLayout(grid)
1123 vbox.addLayout(ok_cancel_buttons(d))
1126 if not d.exec_(): return
1128 password = unicode(pw.text()) if wallet.use_encryption else None
1129 new_password = unicode(new_pw.text())
1130 new_password2 = unicode(conf_pw.text())
1133 seed = wallet.pw_decode( wallet.seed, password)
1135 QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
1138 if new_password != new_password2:
1139 QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
1142 wallet.update_password(seed, password, new_password)
1145 def seed_dialog(wallet, parent=None):
1149 vbox = QVBoxLayout()
1150 msg = _("Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet.")
1151 vbox.addWidget(QLabel(msg))
1153 grid = QGridLayout()
1156 seed_e = QLineEdit()
1157 grid.addWidget(QLabel(_('Seed or mnemonic')), 1, 0)
1158 grid.addWidget(seed_e, 1, 1)
1162 grid.addWidget(QLabel(_('Gap limit')), 2, 0)
1163 grid.addWidget(gap_e, 2, 1)
1164 gap_e.textChanged.connect(lambda: numbify(gap_e,True))
1165 vbox.addLayout(grid)
1167 vbox.addLayout(ok_cancel_buttons(d))
1170 if not d.exec_(): return
1173 gap = int(unicode(gap_e.text()))
1175 QMessageBox.warning(None, _('Error'), 'error', 'OK')
1179 seed = unicode(seed_e.text())
1182 print_error("Warning: Not hex, trying decode")
1184 seed = mnemonic.mn_decode( seed.split(' ') )
1186 QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK'))
1189 QMessageBox.warning(None, _('Error'), _('No seed'), 'OK')
1192 wallet.seed = str(seed)
1193 #print repr(wallet.seed)
1194 wallet.gap_limit = gap
1199 def settings_dialog(self):
1202 vbox = QVBoxLayout()
1203 msg = _('Here are the settings of your wallet.') + '\n'\
1204 + _('For more explanations, click on the help buttons next to each field.')
1207 label.setFixedWidth(250)
1208 label.setWordWrap(True)
1209 label.setAlignment(Qt.AlignJustify)
1210 vbox.addWidget(label)
1212 grid = QGridLayout()
1214 vbox.addLayout(grid)
1217 fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) )
1218 grid.addWidget(QLabel(_('Transaction fee')), 2, 0)
1219 grid.addWidget(fee_e, 2, 1)
1220 msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \
1221 + _('Recommended value') + ': 0.001'
1222 grid.addWidget(HelpButton(msg), 2, 2)
1223 fee_e.textChanged.connect(lambda: numbify(fee_e,False))
1226 nz_e.setText("%d"% self.wallet.num_zeros)
1227 grid.addWidget(QLabel(_('Display zeros')), 3, 0)
1228 msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
1229 grid.addWidget(HelpButton(msg), 3, 2)
1230 grid.addWidget(nz_e, 3, 1)
1231 nz_e.textChanged.connect(lambda: numbify(nz_e,True))
1233 usechange_cb = QCheckBox(_('Use change addresses'))
1234 grid.addWidget(usechange_cb, 5, 0)
1235 usechange_cb.setChecked(self.wallet.use_change)
1236 grid.addWidget(HelpButton(_('Using a change addresses makes it more difficult for other people to track your transactions. ')), 5, 2)
1238 msg = _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \
1239 + _('You may increase it if you need more receiving addresses.') + '\n\n' \
1240 + _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \
1241 + _('Given the current status of your address sequence, the minimum gap limit you can use is: ') + '%d'%self.wallet.min_acceptable_gap() + '\n\n' \
1242 + _('Warning') + ': ' \
1243 + _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \
1244 + _('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'
1246 gap_e.setText("%d"% self.wallet.gap_limit)
1247 grid.addWidget(QLabel(_('Gap limit')), 6, 0)
1248 grid.addWidget(gap_e, 6, 1)
1249 grid.addWidget(HelpButton(msg), 6, 2)
1250 gap_e.textChanged.connect(lambda: numbify(nz_e,True))
1253 gui.addItems(['Lite', 'Qt', 'Gtk'])
1254 gui.setCurrentIndex(gui.findText(self.config.get("gui","lite").capitalize()))
1255 grid.addWidget(QLabel(_('Default GUI') + ':'), 7, 0)
1256 grid.addWidget(gui, 7, 1)
1257 grid.addWidget(HelpButton(_('Select which GUI mode to use at start up. ')), 7, 2)
1259 vbox.addLayout(ok_cancel_buttons(d))
1263 if not d.exec_(): return
1265 fee = unicode(fee_e.text())
1267 fee = int( 100000000 * Decimal(fee) )
1269 QMessageBox.warning(self, _('Error'), _('Invalid value') +': %s'%fee, _('OK'))
1272 if self.wallet.fee != fee:
1273 self.wallet.fee = fee
1276 nz = unicode(nz_e.text())
1281 QMessageBox.warning(self, _('Error'), _('Invalid value')+':%s'%nz, _('OK'))
1284 if self.wallet.num_zeros != nz:
1285 self.wallet.num_zeros = nz
1286 self.update_history_tab()
1287 self.update_receive_tab()
1290 self.wallet.use_change = usechange_cb.isChecked()
1292 n = int(gap_e.text())
1294 QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
1296 if self.wallet.gap_limit != n:
1297 r = self.wallet.change_gap_limit(n)
1299 self.update_receive_tab()
1301 QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
1303 self.config.set_key("gui", str(gui.currentText()).lower(), True)
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 = interface.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() )
1461 if proxy_mode.currentText() != 'NONE':
1462 proxy = { u'mode':unicode(proxy_mode.currentText()).lower(), u'host':unicode(proxy_host.text()), u'port':unicode(proxy_port.text()) }
1466 wallet.config.set_key("proxy", proxy, True)
1467 wallet.config.set_key("server", server, True)
1468 interface.set_server(server, proxy)
1472 def closeEvent(self, event):
1474 self.config.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()], True)
1480 def __init__(self, wallet, config, app=None):
1481 self.wallet = wallet
1482 self.config = config
1484 self.app = QApplication(sys.argv)
1486 def server_list_changed(self):
1489 def waiting_dialog(self):
1495 w.setWindowTitle('Electrum')
1497 vbox = QVBoxLayout()
1502 if self.wallet.up_to_date:
1505 l.setText("Please wait...\nAddresses generated: %d\nKilobytes received: %.1f"\
1506 %(len(self.wallet.all_addresses()), self.wallet.interface.bytes_received/1024.))
1508 w.connect(s, QtCore.SIGNAL('timersignal'), f)
1509 self.wallet.interface.poke()
1514 def restore_or_create(self):
1516 msg = _("Wallet file not found.")+"\n"+_("Do you want to create a new wallet, or to restore an existing one?")
1517 r = QMessageBox.question(None, _('Message'), msg, _('Create'), _('Restore'), _('Cancel'), 0, 2)
1518 if r==2: return False
1520 is_recovery = (r==1)
1521 wallet = self.wallet
1522 # ask for the server.
1523 if not ElectrumWindow.network_dialog( wallet, parent=None ): return False
1526 wallet.new_seed(None)
1527 wallet.init_mpk( wallet.seed )
1528 wallet.up_to_date_event.clear()
1529 wallet.up_to_date = False
1530 self.waiting_dialog()
1531 # run a dialog indicating the seed, ask the user to remember it
1532 ElectrumWindow.show_seed_dialog(wallet)
1534 ElectrumWindow.change_password_dialog(wallet)
1536 # ask for seed and gap.
1537 if not ElectrumWindow.seed_dialog( wallet ): return False
1538 wallet.init_mpk( wallet.seed )
1539 wallet.up_to_date_event.clear()
1540 wallet.up_to_date = False
1541 self.waiting_dialog()
1542 if wallet.is_found():
1543 # history and addressbook
1544 wallet.update_tx_history()
1545 wallet.fill_addressbook()
1546 print "Recovery successful"
1549 QMessageBox.information(None, _('Error'), _("No transactions found for this seed"), _('OK'))
1557 w = ElectrumWindow(self.wallet, self.config)
1558 if url: w.set_url(url)