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)
171 def waiting_dialog(f):
177 w.setWindowTitle('Electrum')
187 w.connect(s, QtCore.SIGNAL('timersignal'), ff)
192 def ok_cancel_buttons(dialog):
195 b = QPushButton("OK")
197 b.clicked.connect(dialog.accept)
198 b = QPushButton("Cancel")
200 b.clicked.connect(dialog.reject)
204 class ElectrumWindow(QMainWindow):
206 def __init__(self, wallet, config):
207 QMainWindow.__init__(self)
210 self.wallet.interface.register_callback('updated', self.update_callback)
211 self.wallet.interface.register_callback('connected', self.update_callback)
212 self.wallet.interface.register_callback('disconnected', self.update_callback)
213 self.wallet.interface.register_callback('disconnecting', self.update_callback)
215 self.detailed_view = config.get('qt_detailed_view', False)
217 self.funds_error = False
218 self.completions = QStringListModel()
220 self.tabs = tabs = QTabWidget(self)
221 tabs.addTab(self.create_history_tab(), _('History') )
223 tabs.addTab(self.create_send_tab(), _('Send') )
224 tabs.addTab(self.create_receive_tab(), _('Receive') )
225 tabs.addTab(self.create_contacts_tab(), _('Contacts') )
226 tabs.addTab(self.create_wall_tab(), _('Wall') )
227 tabs.setMinimumSize(600, 400)
228 tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
229 self.setCentralWidget(tabs)
230 self.create_status_bar()
232 g = self.config.get("winpos-qt",[100, 100, 840, 400])
233 self.setGeometry(g[0], g[1], g[2], g[3])
234 title = 'Electrum ' + self.wallet.electrum_version + ' - ' + self.config.path
235 if not self.wallet.seed: title += ' [seedless]'
236 self.setWindowTitle( title )
238 QShortcut(QKeySequence("Ctrl+W"), self, self.close)
239 QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
240 QShortcut(QKeySequence("Ctrl+PgUp"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() - 1 )%tabs.count() ))
241 QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() + 1 )%tabs.count() ))
243 self.connect(self, QtCore.SIGNAL('updatesignal'), self.update_wallet)
244 self.history_list.setFocus(True)
246 # dark magic fix by flatfly; https://bitcointalk.org/index.php?topic=73651.msg959913#msg959913
247 if platform.system() == 'Windows':
248 n = 3 if self.wallet.seed else 2
249 tabs.setCurrentIndex (n)
250 tabs.setCurrentIndex (0)
253 def connect_slots(self, sender):
255 self.connect(sender, QtCore.SIGNAL('timersignal'), self.check_recipient)
256 self.previous_payto_e=''
258 def check_recipient(self):
259 if self.payto_e.hasFocus():
261 r = unicode( self.payto_e.text() )
262 if r != self.previous_payto_e:
263 self.previous_payto_e = r
265 if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
267 to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
271 s = r + ' <' + to_address + '>'
272 self.payto_e.setText(s)
275 def update_callback(self):
276 self.emit(QtCore.SIGNAL('updatesignal'))
278 def update_wallet(self):
279 if self.wallet.interface and self.wallet.interface.is_connected:
280 if not self.wallet.up_to_date:
281 text = _( "Synchronizing..." )
282 icon = QIcon(":icons/status_waiting.png")
284 c, u = self.wallet.get_balance()
285 text = _( "Balance" ) + ": %s "%( format_satoshis(c,False,self.wallet.num_zeros) )
286 if u: text += "[%s unconfirmed]"%( format_satoshis(u,True,self.wallet.num_zeros).strip() )
287 icon = QIcon(":icons/status_connected.png")
289 text = _( "Not connected" )
290 icon = QIcon(":icons/status_disconnected.png")
293 text = _( "Not enough funds" )
295 self.statusBar().showMessage(text)
296 self.status_button.setIcon( icon )
298 if self.wallet.up_to_date:
299 self.textbox.setText( self.wallet.banner )
300 self.update_history_tab()
301 self.update_receive_tab()
302 self.update_contacts_tab()
303 self.update_completions()
306 def create_history_tab(self):
307 self.history_list = l = MyTreeWidget(self)
309 l.setColumnWidth(0, 40)
310 l.setColumnWidth(1, 140)
311 l.setColumnWidth(2, 350)
312 l.setColumnWidth(3, 140)
313 l.setColumnWidth(4, 140)
314 l.setHeaderLabels( [ '', _( 'Date' ), _( 'To / From' ) , _('Amount'), _('Balance')] )
315 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.tx_label_clicked)
316 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), self.tx_label_changed)
317 l.setContextMenuPolicy(Qt.CustomContextMenu)
318 l.customContextMenuRequested.connect(self.create_history_menu)
321 def create_history_menu(self, position):
322 self.history_list.selectedIndexes()
323 item = self.history_list.currentItem()
325 tx_hash = str(item.toolTip(0))
327 menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash))
328 menu.addAction(_("Details"), lambda: self.tx_details(tx_hash))
329 menu.addAction(_("Edit description"), lambda: self.tx_label_clicked(item,2))
330 menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
332 def tx_details(self, tx_hash):
333 tx = self.wallet.tx_history.get(tx_hash)
336 conf = self.wallet.verifier.get_confirmations(tx_hash)
337 time_str = datetime.datetime.fromtimestamp( tx['timestamp']).isoformat(' ')[:-3]
342 tx_details = _("Transaction Details") +"\n\n" \
343 + "Transaction ID:\n" + tx_hash + "\n\n" \
344 + "Status: %d confirmations\n\n"%conf \
345 + "Date: %s\n\n"%time_str \
346 + "Inputs:\n-"+ '\n-'.join(tx['inputs']) + "\n\n" \
347 + "Outputs:\n-"+ '\n-'.join(tx['outputs'])
349 r = self.wallet.receipts.get(tx_hash)
351 tx_details += "\n_______________________________________" \
352 + '\n\nSigned URI: ' + r[2] \
353 + "\n\nSigned by: " + r[0] \
354 + '\n\nSignature: ' + r[1]
356 QMessageBox.information(self, 'Details', tx_details, 'OK')
359 def tx_label_clicked(self, item, column):
360 if column==2 and item.isSelected():
361 tx_hash = str(item.toolTip(0))
363 #if not self.wallet.labels.get(tx_hash): item.setText(2,'')
364 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
365 self.history_list.editItem( item, column )
366 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
369 def tx_label_changed(self, item, column):
373 tx_hash = str(item.toolTip(0))
374 tx = self.wallet.tx_history.get(tx_hash)
375 s = self.wallet.labels.get(tx_hash)
376 text = unicode( item.text(2) )
378 self.wallet.labels[tx_hash] = text
379 item.setForeground(2, QBrush(QColor('black')))
381 if s: self.wallet.labels.pop(tx_hash)
382 text = tx['default_label']
383 item.setText(2, text)
384 item.setForeground(2, QBrush(QColor('gray')))
387 def edit_label(self, is_recv):
388 l = self.receive_list if is_recv else self.contacts_list
389 c = 2 if is_recv else 1
390 item = l.currentItem()
391 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
392 l.editItem( item, c )
393 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
395 def address_label_clicked(self, item, column, l, column_addr, column_label):
396 if column==column_label and item.isSelected():
397 addr = unicode( item.text(column_addr) )
398 label = unicode( item.text(column_label) )
399 if label in self.wallet.aliases.keys():
401 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
402 l.editItem( item, column )
403 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
405 def address_label_changed(self, item, column, l, column_addr, column_label):
406 addr = unicode( item.text(column_addr) )
407 text = unicode( item.text(column_label) )
411 if text not in self.wallet.aliases.keys():
412 old_addr = self.wallet.labels.get(text)
414 self.wallet.labels[addr] = text
417 print_error("Error: This is one of your aliases")
418 label = self.wallet.labels.get(addr,'')
419 item.setText(column_label, QString(label))
421 s = self.wallet.labels.get(addr)
423 self.wallet.labels.pop(addr)
427 self.wallet.update_tx_labels()
428 self.update_history_tab()
429 self.update_completions()
432 def update_history_tab(self):
433 self.history_list.clear()
435 for tx in self.wallet.get_tx_history():
436 tx_hash = tx['tx_hash']
438 conf = self.wallet.verifier.get_confirmations(tx_hash)
439 time_str = datetime.datetime.fromtimestamp( tx['timestamp']).isoformat(' ')[:-3]
441 icon = QIcon(":icons/clock%d.png"%conf)
443 icon = QIcon(":icons/confirmed.png")
447 icon = QIcon(":icons/unconfirmed.png")
450 label = self.wallet.labels.get(tx_hash)
451 is_default_label = (label == '') or (label is None)
452 if is_default_label: label = tx['default_label']
454 item = QTreeWidgetItem( [ '', time_str, label, format_satoshis(v,True,self.wallet.num_zeros), format_satoshis(balance,False,self.wallet.num_zeros)] )
455 item.setFont(2, QFont(MONOSPACE_FONT))
456 item.setFont(3, QFont(MONOSPACE_FONT))
457 item.setFont(4, QFont(MONOSPACE_FONT))
458 item.setToolTip(0, tx_hash)
460 item.setForeground(2, QBrush(QColor('grey')))
462 item.setIcon(0, icon)
463 self.history_list.insertTopLevelItem(0,item)
465 self.history_list.setCurrentItem(self.history_list.topLevelItem(0))
468 def create_send_tab(self):
473 grid.setColumnMinimumWidth(3,300)
474 grid.setColumnStretch(5,1)
476 self.payto_e = QLineEdit()
477 grid.addWidget(QLabel(_('Pay to')), 1, 0)
478 grid.addWidget(self.payto_e, 1, 1, 1, 3)
481 qrcode = qrscanner.scan_qr()
482 if 'address' in qrcode:
483 self.payto_e.setText(qrcode['address'])
484 if 'amount' in qrcode:
485 self.amount_e.setText(str(qrcode['amount']))
486 if 'label' in qrcode:
487 self.message_e.setText(qrcode['label'])
488 if 'message' in qrcode:
489 self.message_e.setText("%s (%s)" % (self.message_e.text(), qrcode['message']))
492 if qrscanner.is_available():
493 b = QPushButton(_("Scan QR code"))
494 b.clicked.connect(fill_from_qr)
495 grid.addWidget(b, 1, 5)
497 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)
499 completer = QCompleter()
500 completer.setCaseSensitivity(False)
501 self.payto_e.setCompleter(completer)
502 completer.setModel(self.completions)
504 self.message_e = QLineEdit()
505 grid.addWidget(QLabel(_('Description')), 2, 0)
506 grid.addWidget(self.message_e, 2, 1, 1, 3)
507 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)
509 self.amount_e = QLineEdit()
510 grid.addWidget(QLabel(_('Amount')), 3, 0)
511 grid.addWidget(self.amount_e, 3, 1, 1, 2)
512 grid.addWidget(HelpButton(
513 _('Amount to be sent.') + '\n\n' \
514 + _('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)
516 self.fee_e = QLineEdit()
517 grid.addWidget(QLabel(_('Fee')), 4, 0)
518 grid.addWidget(self.fee_e, 4, 1, 1, 2)
519 grid.addWidget(HelpButton(
520 _('Bitcoin transactions are in general not free. A transaction fee is paid by the sender of the funds.') + '\n\n'\
521 + _('The amount of fee can be decided freely by the sender. However, transactions with low fees take more time to be processed.') + '\n\n'\
522 + _('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)
524 b = EnterButton(_("Send"), self.do_send)
525 grid.addWidget(b, 6, 1)
527 b = EnterButton(_("Clear"),self.do_clear)
528 grid.addWidget(b, 6, 2)
530 self.payto_sig = QLabel('')
531 grid.addWidget(self.payto_sig, 7, 0, 1, 4)
533 QShortcut(QKeySequence("Up"), w, w.focusPreviousChild)
534 QShortcut(QKeySequence("Down"), w, w.focusNextChild)
543 def entry_changed( is_fee ):
544 self.funds_error = False
545 amount = numbify(self.amount_e)
546 fee = numbify(self.fee_e)
547 if not is_fee: fee = None
550 inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee )
552 self.fee_e.setText( str( Decimal( fee ) / 100000000 ) )
555 palette.setColor(self.amount_e.foregroundRole(), QColor('black'))
558 palette.setColor(self.amount_e.foregroundRole(), QColor('red'))
559 self.funds_error = True
560 self.amount_e.setPalette(palette)
561 self.fee_e.setPalette(palette)
563 self.amount_e.textChanged.connect(lambda: entry_changed(False) )
564 self.fee_e.textChanged.connect(lambda: entry_changed(True) )
569 def update_completions(self):
571 for addr,label in self.wallet.labels.items():
572 if addr in self.wallet.addressbook:
573 l.append( label + ' <' + addr + '>')
574 l = l + self.wallet.aliases.keys()
576 self.completions.setStringList(l)
582 label = unicode( self.message_e.text() )
583 r = unicode( self.payto_e.text() )
587 m1 = re.match(ALIAS_REGEXP, r)
588 # label or alias, with address in brackets
589 m2 = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
592 to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
596 to_address = m2.group(2)
600 if not self.wallet.is_valid(to_address):
601 QMessageBox.warning(self, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK'))
605 amount = int( Decimal( unicode( self.amount_e.text())) * 100000000 )
607 QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
610 fee = int( Decimal( unicode( self.fee_e.text())) * 100000000 )
612 QMessageBox.warning(self, _('Error'), _('Invalid Fee'), _('OK'))
615 if self.wallet.use_encryption:
616 password = self.password_dialog()
623 tx = self.wallet.mktx( to_address, amount, label, password, fee)
624 except BaseException, e:
625 self.show_message(str(e))
628 h = self.wallet.send_tx(tx)
629 waiting_dialog(lambda: False if self.wallet.tx_event.isSet() else _("Please wait..."))
630 status, msg = self.wallet.receive_tx( h )
633 QMessageBox.information(self, '', _('Payment sent.')+'\n'+msg, _('OK'))
635 self.update_contacts_tab()
637 QMessageBox.warning(self, _('Error'), msg, _('OK'))
640 def set_url(self, url):
641 payto, amount, label, message, signature, identity, url = self.wallet.parse_url(url, self.show_message, self.question)
642 self.tabs.setCurrentIndex(1)
643 label = self.wallet.labels.get(payto)
644 m_addr = label + ' <'+ payto+'>' if label else payto
645 self.payto_e.setText(m_addr)
647 self.message_e.setText(message)
648 self.amount_e.setText(amount)
650 self.set_frozen(self.payto_e,True)
651 self.set_frozen(self.amount_e,True)
652 self.set_frozen(self.message_e,True)
653 self.payto_sig.setText( ' The bitcoin URI was signed by ' + identity )
655 self.payto_sig.setVisible(False)
658 self.payto_sig.setVisible(False)
659 for e in [self.payto_e, self.message_e, self.amount_e, self.fee_e]:
661 self.set_frozen(e,False)
663 def set_frozen(self,entry,frozen):
665 entry.setReadOnly(True)
666 entry.setFrame(False)
668 palette.setColor(entry.backgroundRole(), QColor('lightgray'))
669 entry.setPalette(palette)
671 entry.setReadOnly(False)
674 palette.setColor(entry.backgroundRole(), QColor('white'))
675 entry.setPalette(palette)
678 def toggle_freeze(self,addr):
680 if addr in self.wallet.frozen_addresses:
681 self.wallet.unfreeze(addr)
683 self.wallet.freeze(addr)
684 self.update_receive_tab()
686 def toggle_priority(self,addr):
688 if addr in self.wallet.prioritized_addresses:
689 self.wallet.unprioritize(addr)
691 self.wallet.prioritize(addr)
692 self.update_receive_tab()
695 def create_list_tab(self, headers):
696 "generic tab creation method"
697 l = MyTreeWidget(self)
698 l.setColumnCount( len(headers) )
699 l.setHeaderLabels( headers )
709 vbox.addWidget(buttons)
714 buttons.setLayout(hbox)
719 def create_receive_tab(self):
720 l,w,hbox = self.create_list_tab([_('Flags'), _('Address'), _('Label'), _('Balance'), _('Tx')])
721 l.setContextMenuPolicy(Qt.CustomContextMenu)
722 l.customContextMenuRequested.connect(self.create_receive_menu)
723 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,1,2))
724 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,1,2))
725 self.receive_list = l
726 self.receive_buttons_hbox = hbox
727 self.details_button = EnterButton(self.details_button_text(), self.toggle_detailed_view)
728 hbox.addWidget(self.details_button)
732 def details_button_text(self):
733 return _('Hide details') if self.detailed_view else _('Show details')
735 def toggle_detailed_view(self):
736 self.detailed_view = not self.detailed_view
737 self.config.set_key('qt_detailed_view', self.detailed_view, True)
739 self.details_button.setText(self.details_button_text())
741 self.update_receive_tab()
742 self.update_contacts_tab()
745 def create_contacts_tab(self):
746 l,w,hbox = self.create_list_tab([_('Address'), _('Label'), _('Tx')])
747 l.setContextMenuPolicy(Qt.CustomContextMenu)
748 l.customContextMenuRequested.connect(self.create_contact_menu)
749 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,0,1))
750 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,0,1))
751 self.contacts_list = l
752 self.contacts_buttons_hbox = hbox
753 hbox.addWidget(EnterButton(_("New"), self.new_contact_dialog))
758 def create_receive_menu(self, position):
759 # fixme: this function apparently has a side effect.
760 # if it is not called the menu pops up several times
761 #self.receive_list.selectedIndexes()
763 item = self.receive_list.itemAt(position)
765 addr = unicode(item.text(1))
767 menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
768 menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
769 menu.addAction(_("Edit label"), lambda: self.edit_label(True))
771 t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
772 menu.addAction(t, lambda: self.toggle_freeze(addr))
773 t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
774 menu.addAction(t, lambda: self.toggle_priority(addr))
775 menu.exec_(self.receive_list.viewport().mapToGlobal(position))
778 def payto(self, x, is_alias):
785 label = self.wallet.labels.get(addr)
786 m_addr = label + ' <' + addr + '>' if label else addr
787 self.tabs.setCurrentIndex(1)
788 self.payto_e.setText(m_addr)
789 self.amount_e.setFocus()
791 def delete_contact(self, x, is_alias):
792 if self.question("Do you want to remove %s from your list of contacts?"%x):
793 if not is_alias and x in self.wallet.addressbook:
794 self.wallet.addressbook.remove(x)
795 if x in self.wallet.labels.keys():
796 self.wallet.labels.pop(x)
797 elif is_alias and x in self.wallet.aliases:
798 self.wallet.aliases.pop(x)
799 self.update_history_tab()
800 self.update_contacts_tab()
801 self.update_completions()
803 def create_contact_menu(self, position):
804 # fixme: this function apparently has a side effect.
805 # if it is not called the menu pops up several times
806 #self.contacts_list.selectedIndexes()
808 item = self.contacts_list.itemAt(position)
810 addr = unicode(item.text(0))
811 label = unicode(item.text(1))
812 is_alias = label in self.wallet.aliases.keys()
813 x = label if is_alias else addr
815 menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
816 menu.addAction(_("Pay to"), lambda: self.payto(x, is_alias))
817 menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
819 menu.addAction(_("Edit label"), lambda: self.edit_label(False))
821 menu.addAction(_("View alias details"), lambda: self.show_contact_details(label))
822 menu.addAction(_("Delete"), lambda: self.delete_contact(x,is_alias))
823 menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
826 def update_receive_tab(self):
827 l = self.receive_list
829 l.setColumnHidden(0,not self.detailed_view)
830 l.setColumnHidden(3,not self.detailed_view)
831 l.setColumnHidden(4,not self.detailed_view)
832 l.setColumnWidth(0, 50)
833 l.setColumnWidth(1, 310)
834 l.setColumnWidth(2, 250)
835 l.setColumnWidth(3, 130)
836 l.setColumnWidth(4, 10)
840 for address in self.wallet.all_addresses():
842 if self.wallet.is_change(address) and not self.detailed_view:
845 label = self.wallet.labels.get(address,'')
847 h = self.wallet.history.get(address,[])
849 if not item['is_input'] : n=n+1
853 if address in self.wallet.addresses:
855 if gap > self.wallet.gap_limit:
858 if address in self.wallet.addresses:
861 c, u = self.wallet.get_addr_balance(address)
862 balance = format_satoshis( c + u, False, self.wallet.num_zeros )
863 flags = self.wallet.get_address_flags(address)
864 item = QTreeWidgetItem( [ flags, address, label, balance, tx] )
866 item.setFont(0, QFont(MONOSPACE_FONT))
867 item.setFont(1, QFont(MONOSPACE_FONT))
868 item.setFont(3, QFont(MONOSPACE_FONT))
869 if address in self.wallet.frozen_addresses:
870 item.setBackgroundColor(1, QColor('lightblue'))
871 elif address in self.wallet.prioritized_addresses:
872 item.setBackgroundColor(1, QColor('lightgreen'))
873 if is_red and address in self.wallet.addresses:
874 item.setBackgroundColor(1, QColor('red'))
875 l.addTopLevelItem(item)
877 # we use column 1 because column 0 may be hidden
878 l.setCurrentItem(l.topLevelItem(0),1)
880 def show_contact_details(self, m):
881 a = self.wallet.aliases.get(m)
883 if a[0] in self.wallet.authorities.keys():
884 s = self.wallet.authorities.get(a[0])
887 msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
888 QMessageBox.information(self, 'Alias', msg, 'OK')
890 def update_contacts_tab(self):
892 l = self.contacts_list
894 l.setColumnHidden(2, not self.detailed_view)
895 l.setColumnWidth(0, 350)
896 l.setColumnWidth(1, 330)
897 l.setColumnWidth(2, 100)
900 for alias, v in self.wallet.aliases.items():
902 alias_targets.append(target)
903 item = QTreeWidgetItem( [ target, alias, '-'] )
904 item.setBackgroundColor(0, QColor('lightgray'))
905 l.addTopLevelItem(item)
907 for address in self.wallet.addressbook:
908 if address in alias_targets: continue
909 label = self.wallet.labels.get(address,'')
911 for item in self.wallet.tx_history.values():
912 if address in item['outputs'] : n=n+1
914 item = QTreeWidgetItem( [ address, label, tx] )
915 item.setFont(0, QFont(MONOSPACE_FONT))
916 l.addTopLevelItem(item)
918 l.setCurrentItem(l.topLevelItem(0))
920 def create_wall_tab(self):
921 self.textbox = textbox = QTextEdit(self)
922 textbox.setFont(QFont(MONOSPACE_FONT))
923 textbox.setReadOnly(True)
926 def create_status_bar(self):
928 sb.setFixedHeight(35)
930 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) )
931 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) )
933 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/seed.png"), "Seed", lambda: self.show_seed_dialog(self.wallet, self) ) )
934 self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) )
935 sb.addPermanentWidget( self.status_button )
936 self.setStatusBar(sb)
938 def new_contact_dialog(self):
939 text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':')
940 address = unicode(text)
942 if self.wallet.is_valid(address):
943 self.wallet.addressbook.append(address)
945 self.update_contacts_tab()
946 self.update_history_tab()
947 self.update_completions()
949 QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
952 def show_seed_dialog(wallet, parent=None):
954 QMessageBox.information(parent, _('Message'),
955 _('No seed'), _('OK'))
958 if wallet.use_encryption:
959 password = parent.password_dialog()
966 seed = wallet.pw_decode(wallet.seed, password)
968 QMessageBox.warning(parent, _('Error'),
969 _('Incorrect Password'), _('OK'))
972 dialog = QDialog(None)
974 dialog.setWindowTitle("Electrum")
976 brainwallet = ' '.join(mnemonic.mn_encode(seed))
978 msg = _("Your wallet generation seed is") +":<p>\"" + brainwallet + "\"<p>" \
979 + _("Please write down or memorize these 12 words (order is important).") + " " \
980 + _("This seed will allow you to recover your wallet in case of computer failure.") + "<p>" \
981 + _("WARNING: Never disclose your seed. Never type it on a website.") + "<p>"
983 main_text = QLabel(msg)
984 main_text.setWordWrap(True)
987 logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
994 copy_function = lambda: app.clipboard().setText(brainwallet)
995 copy_button = QPushButton(_("Copy to Clipboard"))
996 copy_button.clicked.connect(copy_function)
998 show_qr_function = lambda: ElectrumWindow.show_seed_qrcode(seed)
999 qr_button = QPushButton(_("View as QR Code"))
1000 qr_button.clicked.connect(show_qr_function)
1002 ok_button = QPushButton(_("OK"))
1003 ok_button.setDefault(True)
1004 ok_button.clicked.connect(dialog.accept)
1006 main_layout = QGridLayout()
1007 main_layout.addWidget(logo, 0, 0)
1008 main_layout.addWidget(main_text, 0, 1, 1, -1)
1009 main_layout.addWidget(copy_button, 1, 1)
1010 main_layout.addWidget(qr_button, 1, 2)
1011 main_layout.addWidget(ok_button, 1, 3)
1012 dialog.setLayout(main_layout)
1017 def show_seed_qrcode(seed):
1021 d.setWindowTitle(_("Seed"))
1022 d.setMinimumSize(270, 300)
1023 vbox = QVBoxLayout()
1024 vbox.addWidget(QRCodeWidget(seed))
1025 hbox = QHBoxLayout()
1027 b = QPushButton(_("OK"))
1029 b.clicked.connect(d.accept)
1031 vbox.addLayout(hbox)
1036 def show_address_qrcode(self,address):
1037 if not address: return
1040 d.setWindowTitle(address)
1041 d.setMinimumSize(270, 350)
1042 vbox = QVBoxLayout()
1043 qrw = QRCodeWidget(address)
1046 hbox = QHBoxLayout()
1047 amount_e = QLineEdit()
1048 hbox.addWidget(QLabel(_('Amount')))
1049 hbox.addWidget(amount_e)
1050 vbox.addLayout(hbox)
1052 #hbox = QHBoxLayout()
1053 #label_e = QLineEdit()
1054 #hbox.addWidget(QLabel('Label'))
1055 #hbox.addWidget(label_e)
1056 #vbox.addLayout(hbox)
1058 def amount_changed():
1059 amount = numbify(amount_e)
1060 #label = str( label_e.getText() )
1061 if amount is not None:
1062 qrw.set_addr('bitcoin:%s?amount=%s'%(address,str( Decimal(amount) /100000000)))
1064 qrw.set_addr( address )
1068 bmp.save_qrcode(qrw.qr, "qrcode.bmp")
1069 self.show_message(_("QR code saved to file") + " 'qrcode.bmp'")
1071 amount_e.textChanged.connect( amount_changed )
1073 hbox = QHBoxLayout()
1075 b = QPushButton(_("Save"))
1076 b.clicked.connect(do_save)
1078 b = QPushButton(_("Close"))
1080 b.clicked.connect(d.accept)
1082 vbox.addLayout(hbox)
1086 def question(self, msg):
1087 return QMessageBox.question(self, _('Message'), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
1089 def show_message(self, msg):
1090 QMessageBox.information(self, _('Message'), msg, _('OK'))
1092 def password_dialog(self ):
1099 vbox = QVBoxLayout()
1100 msg = _('Please enter your password')
1101 vbox.addWidget(QLabel(msg))
1103 grid = QGridLayout()
1105 grid.addWidget(QLabel(_('Password')), 1, 0)
1106 grid.addWidget(pw, 1, 1)
1107 vbox.addLayout(grid)
1109 vbox.addLayout(ok_cancel_buttons(d))
1112 if not d.exec_(): return
1113 return unicode(pw.text())
1120 def change_password_dialog( wallet, parent=None ):
1123 QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
1131 new_pw = QLineEdit()
1132 new_pw.setEchoMode(2)
1133 conf_pw = QLineEdit()
1134 conf_pw.setEchoMode(2)
1136 vbox = QVBoxLayout()
1138 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')
1140 msg = _("Please choose a password to encrypt your wallet keys.")+'\n'+_("Leave these fields empty if you want to disable encryption.")
1141 vbox.addWidget(QLabel(msg))
1143 grid = QGridLayout()
1146 if wallet.use_encryption:
1147 grid.addWidget(QLabel(_('Password')), 1, 0)
1148 grid.addWidget(pw, 1, 1)
1150 grid.addWidget(QLabel(_('New Password')), 2, 0)
1151 grid.addWidget(new_pw, 2, 1)
1153 grid.addWidget(QLabel(_('Confirm Password')), 3, 0)
1154 grid.addWidget(conf_pw, 3, 1)
1155 vbox.addLayout(grid)
1157 vbox.addLayout(ok_cancel_buttons(d))
1160 if not d.exec_(): return
1162 password = unicode(pw.text()) if wallet.use_encryption else None
1163 new_password = unicode(new_pw.text())
1164 new_password2 = unicode(conf_pw.text())
1167 seed = wallet.pw_decode( wallet.seed, password)
1169 QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
1172 if new_password != new_password2:
1173 QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
1176 wallet.update_password(seed, password, new_password)
1179 def seed_dialog(wallet, parent=None):
1183 vbox = QVBoxLayout()
1184 msg = _("Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet.")
1185 vbox.addWidget(QLabel(msg))
1187 grid = QGridLayout()
1190 seed_e = QLineEdit()
1191 grid.addWidget(QLabel(_('Seed or mnemonic')), 1, 0)
1192 grid.addWidget(seed_e, 1, 1)
1196 grid.addWidget(QLabel(_('Gap limit')), 2, 0)
1197 grid.addWidget(gap_e, 2, 1)
1198 gap_e.textChanged.connect(lambda: numbify(gap_e,True))
1199 vbox.addLayout(grid)
1201 vbox.addLayout(ok_cancel_buttons(d))
1204 if not d.exec_(): return
1207 gap = int(unicode(gap_e.text()))
1209 QMessageBox.warning(None, _('Error'), 'error', 'OK')
1213 seed = unicode(seed_e.text())
1216 print_error("Warning: Not hex, trying decode")
1218 seed = mnemonic.mn_decode( seed.split(' ') )
1220 QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK'))
1223 QMessageBox.warning(None, _('Error'), _('No seed'), 'OK')
1226 wallet.seed = str(seed)
1227 #print repr(wallet.seed)
1228 wallet.gap_limit = gap
1233 def settings_dialog(self):
1236 vbox = QVBoxLayout()
1237 msg = _('Here are the settings of your wallet.') + '\n'\
1238 + _('For more explanations, click on the help buttons next to each field.')
1241 label.setFixedWidth(250)
1242 label.setWordWrap(True)
1243 label.setAlignment(Qt.AlignJustify)
1244 vbox.addWidget(label)
1246 grid = QGridLayout()
1248 vbox.addLayout(grid)
1250 fee_label = QLabel(_('Transaction fee'))
1251 grid.addWidget(fee_label, 2, 0)
1253 fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) )
1254 grid.addWidget(fee_e, 2, 1)
1255 msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \
1256 + _('Recommended value') + ': 0.001'
1257 grid.addWidget(HelpButton(msg), 2, 2)
1258 fee_e.textChanged.connect(lambda: numbify(fee_e,False))
1259 if not self.config.is_modifiable('fee'):
1260 for w in [fee_e, fee_label]: w.setEnabled(False)
1262 nz_label = QLabel(_('Display zeros'))
1263 grid.addWidget(nz_label, 3, 0)
1265 nz_e.setText("%d"% self.wallet.num_zeros)
1266 grid.addWidget(nz_e, 3, 1)
1267 msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
1268 grid.addWidget(HelpButton(msg), 3, 2)
1269 nz_e.textChanged.connect(lambda: numbify(nz_e,True))
1270 if not self.config.is_modifiable('num_zeros'):
1271 for w in [nz_e, nz_label]: w.setEnabled(False)
1273 usechange_cb = QCheckBox(_('Use change addresses'))
1274 grid.addWidget(usechange_cb, 5, 0)
1275 usechange_cb.setChecked(self.wallet.use_change)
1276 grid.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions. ')), 5, 2)
1277 if not self.config.is_modifiable('use_change'): usechange_cb.setEnabled(False)
1279 gap_label = QLabel(_('Gap limit'))
1280 grid.addWidget(gap_label, 6, 0)
1282 gap_e.setText("%d"% self.wallet.gap_limit)
1283 grid.addWidget(gap_e, 6, 1)
1284 msg = _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \
1285 + _('You may increase it if you need more receiving addresses.') + '\n\n' \
1286 + _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \
1287 + _('Given the current status of your address sequence, the minimum gap limit you can use is: ') + '%d'%self.wallet.min_acceptable_gap() + '\n\n' \
1288 + _('Warning') + ': ' \
1289 + _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \
1290 + _('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'
1291 grid.addWidget(HelpButton(msg), 6, 2)
1292 gap_e.textChanged.connect(lambda: numbify(nz_e,True))
1293 if not self.config.is_modifiable('gap_limit'):
1294 for w in [gap_e, gap_label]: w.setEnabled(False)
1296 gui_label=QLabel(_('Default GUI') + ':')
1297 grid.addWidget(gui_label , 7, 0)
1298 gui_combo = QComboBox()
1299 gui_combo.addItems(['Lite', 'Classic', 'Gtk', 'Text'])
1300 gui_combo.setCurrentIndex(gui_combo.findText(self.config.get("gui","classic").capitalize()))
1301 grid.addWidget(gui_combo, 7, 1)
1302 grid.addWidget(HelpButton(_('Select which GUI mode to use at start up. ')), 7, 2)
1303 if not self.config.is_modifiable('gui'):
1304 for w in [gui_combo, gui_label]: w.setEnabled(False)
1306 vbox.addLayout(ok_cancel_buttons(d))
1310 if not d.exec_(): return
1312 fee = unicode(fee_e.text())
1314 fee = int( 100000000 * Decimal(fee) )
1316 QMessageBox.warning(self, _('Error'), _('Invalid value') +': %s'%fee, _('OK'))
1319 if self.wallet.fee != fee:
1320 self.wallet.fee = fee
1323 nz = unicode(nz_e.text())
1328 QMessageBox.warning(self, _('Error'), _('Invalid value')+':%s'%nz, _('OK'))
1331 if self.wallet.num_zeros != nz:
1332 self.wallet.num_zeros = nz
1333 self.config.set_key('num_zeros', nz, True)
1334 self.update_history_tab()
1335 self.update_receive_tab()
1337 if self.wallet.use_change != usechange_cb.isChecked():
1338 self.wallet.use_change = usechange_cb.isChecked()
1339 self.config.set_key('use_change', self.wallet.use_change, True)
1342 n = int(gap_e.text())
1344 QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
1347 if self.wallet.gap_limit != n:
1348 r = self.wallet.change_gap_limit(n)
1350 self.update_receive_tab()
1351 self.config.set_key('gap_limit', self.wallet.gap_limit, True)
1353 QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
1355 self.config.set_key("gui", str(gui_combo.currentText()).lower(), True)
1360 def network_dialog(wallet, parent=None):
1361 interface = wallet.interface
1363 if interface.is_connected:
1364 status = _("Connected to")+" %s\n%d blocks"%(interface.host, wallet.verifier.height)
1366 status = _("Not connected")
1369 status = _("Please choose a server.")
1371 server = interface.server
1373 if not wallet.interface.servers:
1375 for x in DEFAULT_SERVERS:
1376 h,port,protocol = x.split(':')
1377 servers_list.append( (h,[(protocol,port)] ) )
1379 servers_list = wallet.interface.servers
1382 for item in servers_list:
1386 _protocol, _port = item2
1387 z[_protocol] = _port
1392 d.setWindowTitle(_('Server'))
1393 d.setMinimumSize(375, 20)
1395 vbox = QVBoxLayout()
1398 hbox = QHBoxLayout()
1400 l.setPixmap(QPixmap(":icons/network.png"))
1403 hbox.addWidget(QLabel(status))
1405 vbox.addLayout(hbox)
1409 grid = QGridLayout()
1411 vbox.addLayout(grid)
1414 server_protocol = QComboBox()
1415 server_host = QLineEdit()
1416 server_host.setFixedWidth(200)
1417 server_port = QLineEdit()
1418 server_port.setFixedWidth(60)
1420 protocol_names = ['TCP', 'HTTP', 'TCP/SSL', 'HTTPS']
1421 protocol_letters = 'thsg'
1422 server_protocol.addItems(protocol_names)
1424 grid.addWidget(QLabel(_('Server') + ':'), 0, 0)
1425 grid.addWidget(server_protocol, 0, 1)
1426 grid.addWidget(server_host, 0, 2)
1427 grid.addWidget(server_port, 0, 3)
1429 host, port, protocol = server.split(':')
1431 def change_protocol(p):
1432 protocol = protocol_letters[p]
1433 host = unicode(server_host.text())
1435 if protocol not in pp.keys():
1436 protocol = pp.keys()[0]
1438 server_host.setText( host )
1439 server_port.setText( port )
1441 server_protocol.connect(server_protocol, SIGNAL('currentIndexChanged(int)'), change_protocol)
1443 label = _('Active Servers') if wallet.interface.servers else _('Default Servers')
1444 servers_list_widget = QTreeWidget(parent)
1445 servers_list_widget.setHeaderLabels( [ label ] )
1446 servers_list_widget.setMaximumHeight(150)
1447 for _host in plist.keys():
1448 servers_list_widget.addTopLevelItem(QTreeWidgetItem( [ _host ] ))
1451 def change_server(host, protocol=None):
1452 pp = plist.get(host,{})
1454 port = pp.get(protocol)
1455 if not port: protocol = None
1461 elif 't' in pp.keys():
1463 port = pp.get(protocol)
1465 protocol = pp.keys()[0]
1466 port = pp.get(protocol)
1469 server_host.setText( host )
1470 server_port.setText( port )
1471 server_protocol.setCurrentIndex(protocol_letters.index(protocol))
1473 for p in protocol_letters:
1474 i = protocol_letters.index(p)
1475 j = server_protocol.model().index(i,0)
1476 if p not in pp.keys():
1477 server_protocol.model().setData(j, QtCore.QVariant(0), QtCore.Qt.UserRole-1)
1479 server_protocol.model().setData(j, QtCore.QVariant(0,False), QtCore.Qt.UserRole-1)
1481 change_server(host,protocol)
1484 servers_list_widget.connect(servers_list_widget, SIGNAL('itemClicked(QTreeWidgetItem*, int)'), lambda x: change_server(unicode(x.text(0))))
1485 grid.addWidget(servers_list_widget, 1, 1, 1, 3)
1487 if not wallet.config.is_modifiable('server'):
1488 for w in [server_host, server_port, server_protocol, servers_list_widget]: w.setEnabled(False)
1491 proxy_mode = QComboBox()
1492 proxy_host = QLineEdit()
1493 proxy_host.setFixedWidth(200)
1494 proxy_port = QLineEdit()
1495 proxy_port.setFixedWidth(60)
1496 proxy_mode.addItems(['NONE', 'SOCKS4', 'SOCKS5', 'HTTP'])
1498 def check_for_disable(index = False):
1499 if proxy_mode.currentText() != 'NONE':
1500 proxy_host.setEnabled(True)
1501 proxy_port.setEnabled(True)
1503 proxy_host.setEnabled(False)
1504 proxy_port.setEnabled(False)
1507 proxy_mode.connect(proxy_mode, SIGNAL('currentIndexChanged(int)'), check_for_disable)
1509 if not wallet.config.is_modifiable('proxy'):
1510 for w in [proxy_host, proxy_port, proxy_mode]: w.setEnabled(False)
1512 proxy_config = interface.proxy if interface.proxy else { "mode":"none", "host":"localhost", "port":"8080"}
1513 proxy_mode.setCurrentIndex(proxy_mode.findText(str(proxy_config.get("mode").upper())))
1514 proxy_host.setText(proxy_config.get("host"))
1515 proxy_port.setText(proxy_config.get("port"))
1517 grid.addWidget(QLabel(_('Proxy') + ':'), 2, 0)
1518 grid.addWidget(proxy_mode, 2, 1)
1519 grid.addWidget(proxy_host, 2, 2)
1520 grid.addWidget(proxy_port, 2, 3)
1523 vbox.addLayout(ok_cancel_buttons(d))
1526 if not d.exec_(): return
1528 server = unicode( server_host.text() ) + ':' + unicode( server_port.text() ) + ':' + (protocol_letters[server_protocol.currentIndex()])
1529 if proxy_mode.currentText() != 'NONE':
1530 proxy = { u'mode':unicode(proxy_mode.currentText()).lower(), u'host':unicode(proxy_host.text()), u'port':unicode(proxy_port.text()) }
1534 wallet.config.set_key("proxy", proxy, True)
1535 wallet.config.set_key("server", server, True)
1536 interface.set_server(server, proxy)
1540 def closeEvent(self, event):
1542 self.config.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()], True)
1548 def __init__(self, wallet, config, app=None):
1549 self.wallet = wallet
1550 self.config = config
1552 self.app = QApplication(sys.argv)
1554 def server_list_changed(self):
1558 def restore_or_create(self):
1560 msg = _("Wallet file not found.")+"\n"+_("Do you want to create a new wallet, or to restore an existing one?")
1561 r = QMessageBox.question(None, _('Message'), msg, _('Create'), _('Restore'), _('Cancel'), 0, 2)
1562 if r==2: return False
1564 is_recovery = (r==1)
1565 wallet = self.wallet
1566 # ask for the server.
1567 if not ElectrumWindow.network_dialog( wallet, parent=None ): return False
1569 # wait until we are connected, because the user might have selected another server
1570 if not wallet.interface.is_connected:
1571 waiting = lambda: False if wallet.interface.is_connected else "connecting...\n"
1572 waiting_dialog(waiting)
1574 waiting = lambda: False if wallet.up_to_date else "Please wait...\nAddresses generated: %d\nKilobytes received: %.1f"\
1575 %(len(wallet.all_addresses()), wallet.interface.bytes_received/1024.)
1578 wallet.new_seed(None)
1579 wallet.init_mpk( wallet.seed )
1580 wallet.up_to_date_event.clear()
1581 wallet.up_to_date = False
1582 wallet.interface.poke('synchronizer')
1583 waiting_dialog(waiting)
1584 # run a dialog indicating the seed, ask the user to remember it
1585 ElectrumWindow.show_seed_dialog(wallet)
1587 ElectrumWindow.change_password_dialog(wallet)
1589 # ask for seed and gap.
1590 if not ElectrumWindow.seed_dialog( wallet ): return False
1591 wallet.init_mpk( wallet.seed )
1592 wallet.up_to_date_event.clear()
1593 wallet.up_to_date = False
1594 wallet.interface.poke('synchronizer')
1595 waiting_dialog(waiting)
1596 if wallet.is_found():
1597 # history and addressbook
1598 wallet.update_tx_history()
1599 wallet.fill_addressbook()
1600 print "Recovery successful"
1603 QMessageBox.information(None, _('Error'), _("No transactions found for this seed"), _('OK'))
1611 w = ElectrumWindow(self.wallet, self.config)
1612 if url: w.set_url(url)