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.register_callback(self.update_callback)
212 self.detailed_view = config.get('qt_detailed_view', False)
214 self.funds_error = False
215 self.completions = QStringListModel()
217 self.tabs = tabs = QTabWidget(self)
218 tabs.addTab(self.create_history_tab(), _('History') )
220 tabs.addTab(self.create_send_tab(), _('Send') )
221 tabs.addTab(self.create_receive_tab(), _('Receive') )
222 tabs.addTab(self.create_contacts_tab(), _('Contacts') )
223 tabs.addTab(self.create_wall_tab(), _('Wall') )
224 tabs.setMinimumSize(600, 400)
225 tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
226 self.setCentralWidget(tabs)
227 self.create_status_bar()
229 g = self.config.get("winpos-qt",[100, 100, 840, 400])
230 self.setGeometry(g[0], g[1], g[2], g[3])
231 title = 'Electrum ' + self.wallet.electrum_version + ' - ' + self.config.path
232 if not self.wallet.seed: title += ' [seedless]'
233 self.setWindowTitle( title )
235 QShortcut(QKeySequence("Ctrl+W"), self, self.close)
236 QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
237 QShortcut(QKeySequence("Ctrl+PgUp"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() - 1 )%tabs.count() ))
238 QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() + 1 )%tabs.count() ))
240 self.connect(self, QtCore.SIGNAL('updatesignal'), self.update_wallet)
241 self.history_list.setFocus(True)
243 # dark magic fix by flatfly; https://bitcointalk.org/index.php?topic=73651.msg959913#msg959913
244 if platform.system() == 'Windows':
245 n = 3 if self.wallet.seed else 2
246 tabs.setCurrentIndex (n)
247 tabs.setCurrentIndex (0)
250 def connect_slots(self, sender):
252 self.connect(sender, QtCore.SIGNAL('timersignal'), self.check_recipient)
253 self.previous_payto_e=''
255 def check_recipient(self):
256 if self.payto_e.hasFocus():
258 r = unicode( self.payto_e.text() )
259 if r != self.previous_payto_e:
260 self.previous_payto_e = r
262 if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
264 to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
268 s = r + ' <' + to_address + '>'
269 self.payto_e.setText(s)
272 def update_callback(self):
273 self.emit(QtCore.SIGNAL('updatesignal'))
275 def update_wallet(self):
276 if self.wallet.interface and self.wallet.interface.is_connected:
277 if self.wallet.blocks == -1:
278 text = _( "Connecting..." )
279 icon = QIcon(":icons/status_disconnected.png")
280 elif self.wallet.blocks == 0:
281 text = _( "Server not ready" )
282 icon = QIcon(":icons/status_disconnected.png")
283 elif not self.wallet.up_to_date:
284 text = _( "Synchronizing..." )
285 icon = QIcon(":icons/status_waiting.png")
287 c, u = self.wallet.get_balance()
288 text = _( "Balance" ) + ": %s "%( format_satoshis(c,False,self.wallet.num_zeros) )
289 if u: text += "[%s unconfirmed]"%( format_satoshis(u,True,self.wallet.num_zeros).strip() )
290 icon = QIcon(":icons/status_connected.png")
292 text = _( "Not connected" )
293 icon = QIcon(":icons/status_disconnected.png")
296 text = _( "Not enough funds" )
298 self.statusBar().showMessage(text)
299 self.status_button.setIcon( icon )
301 if self.wallet.up_to_date:
302 self.textbox.setText( self.wallet.banner )
303 self.update_history_tab()
304 self.update_receive_tab()
305 self.update_contacts_tab()
306 self.update_completions()
309 def create_history_tab(self):
310 self.history_list = l = MyTreeWidget(self)
312 l.setColumnWidth(0, 40)
313 l.setColumnWidth(1, 140)
314 l.setColumnWidth(2, 350)
315 l.setColumnWidth(3, 140)
316 l.setColumnWidth(4, 140)
317 l.setHeaderLabels( [ '', _( 'Date' ), _( 'To / From' ) , _('Amount'), _('Balance')] )
318 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.tx_label_clicked)
319 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), self.tx_label_changed)
320 l.setContextMenuPolicy(Qt.CustomContextMenu)
321 l.customContextMenuRequested.connect(self.create_history_menu)
324 def create_history_menu(self, position):
325 self.history_list.selectedIndexes()
326 item = self.history_list.currentItem()
328 tx_hash = str(item.toolTip(0))
330 menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash))
331 menu.addAction(_("Details"), lambda: self.tx_details(tx_hash))
332 menu.addAction(_("Edit description"), lambda: self.tx_label_clicked(item,2))
333 menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
335 def tx_details(self, tx_hash):
336 tx = self.wallet.tx_history.get(tx_hash)
339 conf = self.wallet.blocks - tx['height'] + 1
340 time_str = datetime.datetime.fromtimestamp( tx['timestamp']).isoformat(' ')[:-3]
345 tx_details = _("Transaction Details") +"\n\n" \
346 + "Transaction ID:\n" + tx_hash + "\n\n" \
347 + "Status: %d confirmations\n\n"%conf \
348 + "Date: %s\n\n"%time_str \
349 + "Inputs:\n-"+ '\n-'.join(tx['inputs']) + "\n\n" \
350 + "Outputs:\n-"+ '\n-'.join(tx['outputs'])
352 r = self.wallet.receipts.get(tx_hash)
354 tx_details += "\n_______________________________________" \
355 + '\n\nSigned URI: ' + r[2] \
356 + "\n\nSigned by: " + r[0] \
357 + '\n\nSignature: ' + r[1]
359 QMessageBox.information(self, 'Details', tx_details, 'OK')
362 def tx_label_clicked(self, item, column):
363 if column==2 and item.isSelected():
364 tx_hash = str(item.toolTip(0))
366 #if not self.wallet.labels.get(tx_hash): item.setText(2,'')
367 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
368 self.history_list.editItem( item, column )
369 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
372 def tx_label_changed(self, item, column):
376 tx_hash = str(item.toolTip(0))
377 tx = self.wallet.tx_history.get(tx_hash)
378 s = self.wallet.labels.get(tx_hash)
379 text = unicode( item.text(2) )
381 self.wallet.labels[tx_hash] = text
382 item.setForeground(2, QBrush(QColor('black')))
384 if s: self.wallet.labels.pop(tx_hash)
385 text = tx['default_label']
386 item.setText(2, text)
387 item.setForeground(2, QBrush(QColor('gray')))
390 def edit_label(self, is_recv):
391 l = self.receive_list if is_recv else self.contacts_list
392 c = 2 if is_recv else 1
393 item = l.currentItem()
394 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
395 l.editItem( item, c )
396 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
398 def address_label_clicked(self, item, column, l, column_addr, column_label):
399 if column==column_label and item.isSelected():
400 addr = unicode( item.text(column_addr) )
401 label = unicode( item.text(column_label) )
402 if label in self.wallet.aliases.keys():
404 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
405 l.editItem( item, column )
406 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
408 def address_label_changed(self, item, column, l, column_addr, column_label):
409 addr = unicode( item.text(column_addr) )
410 text = unicode( item.text(column_label) )
412 if text not in self.wallet.aliases.keys():
413 self.wallet.labels[addr] = text
415 print_error("Error: This is one of your aliases")
416 label = self.wallet.labels.get(addr,'')
417 item.setText(column_label, QString(label))
419 s = self.wallet.labels.get(addr)
420 if s: self.wallet.labels.pop(addr)
422 self.update_history_tab()
423 self.update_completions()
425 def update_history_tab(self):
426 self.history_list.clear()
428 for tx in self.wallet.get_tx_history():
429 tx_hash = tx['tx_hash']
431 conf = self.wallet.blocks - tx['height'] + 1
432 time_str = datetime.datetime.fromtimestamp( tx['timestamp']).isoformat(' ')[:-3]
434 icon = QIcon(":icons/clock%d.png"%conf)
436 icon = QIcon(":icons/confirmed.png")
440 icon = QIcon(":icons/unconfirmed.png")
443 label = self.wallet.labels.get(tx_hash)
444 is_default_label = (label == '') or (label is None)
445 if is_default_label: label = tx['default_label']
447 item = QTreeWidgetItem( [ '', time_str, label, format_satoshis(v,True,self.wallet.num_zeros), format_satoshis(balance,False,self.wallet.num_zeros)] )
448 item.setFont(2, QFont(MONOSPACE_FONT))
449 item.setFont(3, QFont(MONOSPACE_FONT))
450 item.setFont(4, QFont(MONOSPACE_FONT))
451 item.setToolTip(0, tx_hash)
453 item.setForeground(2, QBrush(QColor('grey')))
455 item.setIcon(0, icon)
456 self.history_list.insertTopLevelItem(0,item)
458 self.history_list.setCurrentItem(self.history_list.topLevelItem(0))
461 def create_send_tab(self):
466 grid.setColumnMinimumWidth(3,300)
467 grid.setColumnStretch(5,1)
469 self.payto_e = QLineEdit()
470 grid.addWidget(QLabel(_('Pay to')), 1, 0)
471 grid.addWidget(self.payto_e, 1, 1, 1, 3)
474 qrcode = qrscanner.scan_qr()
475 if 'address' in qrcode:
476 self.payto_e.setText(qrcode['address'])
477 if 'amount' in qrcode:
478 self.amount_e.setText(str(qrcode['amount']))
479 if 'label' in qrcode:
480 self.message_e.setText(qrcode['label'])
481 if 'message' in qrcode:
482 self.message_e.setText("%s (%s)" % (self.message_e.text(), qrcode['message']))
485 if qrscanner.is_available():
486 b = QPushButton(_("Scan QR code"))
487 b.clicked.connect(fill_from_qr)
488 grid.addWidget(b, 1, 5)
490 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)
492 completer = QCompleter()
493 completer.setCaseSensitivity(False)
494 self.payto_e.setCompleter(completer)
495 completer.setModel(self.completions)
497 self.message_e = QLineEdit()
498 grid.addWidget(QLabel(_('Description')), 2, 0)
499 grid.addWidget(self.message_e, 2, 1, 1, 3)
500 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)
502 self.amount_e = QLineEdit()
503 grid.addWidget(QLabel(_('Amount')), 3, 0)
504 grid.addWidget(self.amount_e, 3, 1, 1, 2)
505 grid.addWidget(HelpButton(
506 _('Amount to be sent.') + '\n\n' \
507 + _('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)
509 self.fee_e = QLineEdit()
510 grid.addWidget(QLabel(_('Fee')), 4, 0)
511 grid.addWidget(self.fee_e, 4, 1, 1, 2)
512 grid.addWidget(HelpButton(
513 _('Bitcoin transactions are in general not free. A transaction fee is paid by the sender of the funds.') + '\n\n'\
514 + _('The amount of fee can be decided freely by the sender. However, transactions with low fees take more time to be processed.') + '\n\n'\
515 + _('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)
517 b = EnterButton(_("Send"), self.do_send)
518 grid.addWidget(b, 6, 1)
520 b = EnterButton(_("Clear"),self.do_clear)
521 grid.addWidget(b, 6, 2)
523 self.payto_sig = QLabel('')
524 grid.addWidget(self.payto_sig, 7, 0, 1, 4)
526 QShortcut(QKeySequence("Up"), w, w.focusPreviousChild)
527 QShortcut(QKeySequence("Down"), w, w.focusNextChild)
536 def entry_changed( is_fee ):
537 self.funds_error = False
538 amount = numbify(self.amount_e)
539 fee = numbify(self.fee_e)
540 if not is_fee: fee = None
543 inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee )
545 self.fee_e.setText( str( Decimal( fee ) / 100000000 ) )
548 palette.setColor(self.amount_e.foregroundRole(), QColor('black'))
551 palette.setColor(self.amount_e.foregroundRole(), QColor('red'))
552 self.funds_error = True
553 self.amount_e.setPalette(palette)
554 self.fee_e.setPalette(palette)
556 self.amount_e.textChanged.connect(lambda: entry_changed(False) )
557 self.fee_e.textChanged.connect(lambda: entry_changed(True) )
562 def update_completions(self):
564 for addr,label in self.wallet.labels.items():
565 if addr in self.wallet.addressbook:
566 l.append( label + ' <' + addr + '>')
567 l = l + self.wallet.aliases.keys()
569 self.completions.setStringList(l)
575 label = unicode( self.message_e.text() )
576 r = unicode( self.payto_e.text() )
580 m1 = re.match(ALIAS_REGEXP, r)
581 # label or alias, with address in brackets
582 m2 = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
585 to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
589 to_address = m2.group(2)
593 if not self.wallet.is_valid(to_address):
594 QMessageBox.warning(self, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK'))
598 amount = int( Decimal( unicode( self.amount_e.text())) * 100000000 )
600 QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
603 fee = int( Decimal( unicode( self.fee_e.text())) * 100000000 )
605 QMessageBox.warning(self, _('Error'), _('Invalid Fee'), _('OK'))
608 if self.wallet.use_encryption:
609 password = self.password_dialog()
616 tx = self.wallet.mktx( to_address, amount, label, password, fee)
617 except BaseException, e:
618 self.show_message(str(e))
621 h = self.wallet.send_tx(tx)
622 waiting_dialog(lambda: False if self.wallet.tx_event.isSet() else _("Please wait..."))
623 status, msg = self.wallet.receive_tx( h )
626 QMessageBox.information(self, '', _('Payment sent.')+'\n'+msg, _('OK'))
628 self.update_contacts_tab()
630 QMessageBox.warning(self, _('Error'), msg, _('OK'))
633 def set_url(self, url):
634 payto, amount, label, message, signature, identity, url = self.wallet.parse_url(url, self.show_message, self.question)
635 self.tabs.setCurrentIndex(1)
636 label = self.wallet.labels.get(payto)
637 m_addr = label + ' <'+ payto+'>' if label else payto
638 self.payto_e.setText(m_addr)
640 self.message_e.setText(message)
641 self.amount_e.setText(amount)
643 self.set_frozen(self.payto_e,True)
644 self.set_frozen(self.amount_e,True)
645 self.set_frozen(self.message_e,True)
646 self.payto_sig.setText( ' The bitcoin URI was signed by ' + identity )
648 self.payto_sig.setVisible(False)
651 self.payto_sig.setVisible(False)
652 for e in [self.payto_e, self.message_e, self.amount_e, self.fee_e]:
654 self.set_frozen(e,False)
656 def set_frozen(self,entry,frozen):
658 entry.setReadOnly(True)
659 entry.setFrame(False)
661 palette.setColor(entry.backgroundRole(), QColor('lightgray'))
662 entry.setPalette(palette)
664 entry.setReadOnly(False)
667 palette.setColor(entry.backgroundRole(), QColor('white'))
668 entry.setPalette(palette)
671 def toggle_freeze(self,addr):
673 if addr in self.wallet.frozen_addresses:
674 self.wallet.unfreeze(addr)
676 self.wallet.freeze(addr)
677 self.update_receive_tab()
679 def toggle_priority(self,addr):
681 if addr in self.wallet.prioritized_addresses:
682 self.wallet.unprioritize(addr)
684 self.wallet.prioritize(addr)
685 self.update_receive_tab()
688 def create_list_tab(self, headers):
689 "generic tab creation method"
690 l = MyTreeWidget(self)
691 l.setColumnCount( len(headers) )
692 l.setHeaderLabels( headers )
702 vbox.addWidget(buttons)
707 buttons.setLayout(hbox)
712 def create_receive_tab(self):
713 l,w,hbox = self.create_list_tab([_('Flags'), _('Address'), _('Label'), _('Balance'), _('Tx')])
714 l.setContextMenuPolicy(Qt.CustomContextMenu)
715 l.customContextMenuRequested.connect(self.create_receive_menu)
716 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,1,2))
717 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,1,2))
718 self.receive_list = l
719 self.receive_buttons_hbox = hbox
720 self.details_button = EnterButton(self.details_button_text(), self.toggle_detailed_view)
721 hbox.addWidget(self.details_button)
725 def details_button_text(self):
726 return _('Hide details') if self.detailed_view else _('Show details')
728 def toggle_detailed_view(self):
729 self.detailed_view = not self.detailed_view
730 self.config.set_key('qt_detailed_view', self.detailed_view, True)
732 self.details_button.setText(self.details_button_text())
734 self.update_receive_tab()
735 self.update_contacts_tab()
738 def create_contacts_tab(self):
739 l,w,hbox = self.create_list_tab([_('Address'), _('Label'), _('Tx')])
740 l.setContextMenuPolicy(Qt.CustomContextMenu)
741 l.customContextMenuRequested.connect(self.create_contact_menu)
742 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,0,1))
743 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,0,1))
744 self.contacts_list = l
745 self.contacts_buttons_hbox = hbox
746 hbox.addWidget(EnterButton(_("New"), self.new_contact_dialog))
751 def create_receive_menu(self, position):
752 # fixme: this function apparently has a side effect.
753 # if it is not called the menu pops up several times
754 #self.receive_list.selectedIndexes()
756 item = self.receive_list.itemAt(position)
758 addr = unicode(item.text(1))
760 menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
761 menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
762 menu.addAction(_("Edit label"), lambda: self.edit_label(True))
764 t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
765 menu.addAction(t, lambda: self.toggle_freeze(addr))
766 t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
767 menu.addAction(t, lambda: self.toggle_priority(addr))
768 menu.exec_(self.receive_list.viewport().mapToGlobal(position))
771 def payto(self, x, is_alias):
778 label = self.wallet.labels.get(addr)
779 m_addr = label + ' <' + addr + '>' if label else addr
780 self.tabs.setCurrentIndex(1)
781 self.payto_e.setText(m_addr)
782 self.amount_e.setFocus()
784 def delete_contact(self, x, is_alias):
785 if self.question("Do you want to remove %s from your list of contacts?"%x):
786 if not is_alias and x in self.wallet.addressbook:
787 self.wallet.addressbook.remove(x)
788 if x in self.wallet.labels.keys():
789 self.wallet.labels.pop(x)
790 elif is_alias and x in self.wallet.aliases:
791 self.wallet.aliases.pop(x)
792 self.update_history_tab()
793 self.update_contacts_tab()
794 self.update_completions()
796 def create_contact_menu(self, position):
797 # fixme: this function apparently has a side effect.
798 # if it is not called the menu pops up several times
799 #self.contacts_list.selectedIndexes()
801 item = self.contacts_list.itemAt(position)
803 addr = unicode(item.text(0))
804 label = unicode(item.text(1))
805 is_alias = label in self.wallet.aliases.keys()
806 x = label if is_alias else addr
808 menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
809 menu.addAction(_("Pay to"), lambda: self.payto(x, is_alias))
810 menu.addAction(_("View QR code"),lambda: self.show_address_qrcode(addr))
812 menu.addAction(_("Edit label"), lambda: self.edit_label(False))
814 menu.addAction(_("View alias details"), lambda: self.show_contact_details(label))
815 menu.addAction(_("Delete"), lambda: self.delete_contact(x,is_alias))
816 menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
819 def update_receive_tab(self):
820 l = self.receive_list
822 l.setColumnHidden(0,not self.detailed_view)
823 l.setColumnHidden(3,not self.detailed_view)
824 l.setColumnHidden(4,not self.detailed_view)
825 l.setColumnWidth(0, 50)
826 l.setColumnWidth(1, 310)
827 l.setColumnWidth(2, 250)
828 l.setColumnWidth(3, 130)
829 l.setColumnWidth(4, 10)
833 for address in self.wallet.all_addresses():
835 if self.wallet.is_change(address) and not self.detailed_view:
838 label = self.wallet.labels.get(address,'')
840 h = self.wallet.history.get(address,[])
842 if not item['is_input'] : n=n+1
846 if address in self.wallet.addresses:
848 if gap > self.wallet.gap_limit:
851 if address in self.wallet.addresses:
854 c, u = self.wallet.get_addr_balance(address)
855 balance = format_satoshis( c + u, False, self.wallet.num_zeros )
856 flags = self.wallet.get_address_flags(address)
857 item = QTreeWidgetItem( [ flags, address, label, balance, tx] )
859 item.setFont(0, QFont(MONOSPACE_FONT))
860 item.setFont(1, QFont(MONOSPACE_FONT))
861 item.setFont(3, QFont(MONOSPACE_FONT))
862 if address in self.wallet.frozen_addresses:
863 item.setBackgroundColor(1, QColor('lightblue'))
864 elif address in self.wallet.prioritized_addresses:
865 item.setBackgroundColor(1, QColor('lightgreen'))
866 if is_red and address in self.wallet.addresses:
867 item.setBackgroundColor(1, QColor('red'))
868 l.addTopLevelItem(item)
870 # we use column 1 because column 0 may be hidden
871 l.setCurrentItem(l.topLevelItem(0),1)
873 def show_contact_details(self, m):
874 a = self.wallet.aliases.get(m)
876 if a[0] in self.wallet.authorities.keys():
877 s = self.wallet.authorities.get(a[0])
880 msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
881 QMessageBox.information(self, 'Alias', msg, 'OK')
883 def update_contacts_tab(self):
885 l = self.contacts_list
887 l.setColumnHidden(2, not self.detailed_view)
888 l.setColumnWidth(0, 350)
889 l.setColumnWidth(1, 330)
890 l.setColumnWidth(2, 100)
893 for alias, v in self.wallet.aliases.items():
895 alias_targets.append(target)
896 item = QTreeWidgetItem( [ target, alias, '-'] )
897 item.setBackgroundColor(0, QColor('lightgray'))
898 l.addTopLevelItem(item)
900 for address in self.wallet.addressbook:
901 if address in alias_targets: continue
902 label = self.wallet.labels.get(address,'')
904 for item in self.wallet.tx_history.values():
905 if address in item['outputs'] : n=n+1
907 item = QTreeWidgetItem( [ address, label, tx] )
908 item.setFont(0, QFont(MONOSPACE_FONT))
909 l.addTopLevelItem(item)
911 l.setCurrentItem(l.topLevelItem(0))
913 def create_wall_tab(self):
914 self.textbox = textbox = QTextEdit(self)
915 textbox.setFont(QFont(MONOSPACE_FONT))
916 textbox.setReadOnly(True)
919 def create_status_bar(self):
921 sb.setFixedHeight(35)
923 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) )
924 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) )
926 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/seed.png"), "Seed", lambda: self.show_seed_dialog(self.wallet, self) ) )
927 self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) )
928 sb.addPermanentWidget( self.status_button )
929 self.setStatusBar(sb)
931 def new_contact_dialog(self):
932 text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':')
933 address = unicode(text)
935 if self.wallet.is_valid(address):
936 self.wallet.addressbook.append(address)
938 self.update_contacts_tab()
939 self.update_history_tab()
940 self.update_completions()
942 QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
945 def show_seed_dialog(wallet, parent=None):
947 QMessageBox.information(parent, _('Message'),
948 _('No seed'), _('OK'))
951 if wallet.use_encryption:
952 password = parent.password_dialog()
959 seed = wallet.pw_decode(wallet.seed, password)
961 QMessageBox.warning(parent, _('Error'),
962 _('Incorrect Password'), _('OK'))
965 dialog = QDialog(None)
967 dialog.setWindowTitle(_("Seed"))
969 brainwallet = ' '.join(mnemonic.mn_encode(seed))
971 msg = _("Your wallet generation seed is") +":<p>\"" + brainwallet + "\"<p>" \
972 + _("Please write down or memorize these 12 words (order is important).") + " " \
973 + _("This seed will allow you to recover your wallet in case of computer failure.") + "<p>" \
974 + _("WARNING: Never disclose your seed. Never type it on a website.") + "<p>"
976 main_text = QLabel(msg)
977 main_text.setWordWrap(True)
980 logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
987 copy_function = lambda: app.clipboard().setText(brainwallet)
988 copy_button = QPushButton(_("Copy to Clipboard"))
989 copy_button.clicked.connect(copy_function)
991 show_qr_function = lambda: ElectrumWindow.show_seed_qrcode(seed)
992 qr_button = QPushButton(_("View as QR Code"))
993 qr_button.clicked.connect(show_qr_function)
995 ok_button = QPushButton(_("OK"))
996 ok_button.clicked.connect(dialog.accept)
998 main_layout = QGridLayout()
999 main_layout.addWidget(logo, 0, 0)
1000 main_layout.addWidget(main_text, 0, 1, 1, -1)
1001 main_layout.addWidget(copy_button, 1, 1)
1002 main_layout.addWidget(qr_button, 1, 2)
1003 main_layout.addWidget(ok_button, 1, 3)
1004 dialog.setLayout(main_layout)
1009 def show_seed_qrcode(seed):
1013 d.setWindowTitle(_("Seed"))
1014 d.setMinimumSize(270, 300)
1015 vbox = QVBoxLayout()
1016 vbox.addWidget(QRCodeWidget(seed))
1017 hbox = QHBoxLayout()
1019 b = QPushButton(_("OK"))
1021 b.clicked.connect(d.accept)
1023 vbox.addLayout(hbox)
1028 def show_address_qrcode(self,address):
1029 if not address: return
1032 d.setWindowTitle(address)
1033 d.setMinimumSize(270, 350)
1034 vbox = QVBoxLayout()
1035 qrw = QRCodeWidget(address)
1038 hbox = QHBoxLayout()
1039 amount_e = QLineEdit()
1040 hbox.addWidget(QLabel(_('Amount')))
1041 hbox.addWidget(amount_e)
1042 vbox.addLayout(hbox)
1044 #hbox = QHBoxLayout()
1045 #label_e = QLineEdit()
1046 #hbox.addWidget(QLabel('Label'))
1047 #hbox.addWidget(label_e)
1048 #vbox.addLayout(hbox)
1050 def amount_changed():
1051 amount = numbify(amount_e)
1052 #label = str( label_e.getText() )
1053 if amount is not None:
1054 qrw.set_addr('bitcoin:%s?amount=%s'%(address,str( Decimal(amount) /100000000)))
1056 qrw.set_addr( address )
1060 bmp.save_qrcode(qrw.qr, "qrcode.bmp")
1061 self.show_message(_("QR code saved to file") + " 'qrcode.bmp'")
1063 amount_e.textChanged.connect( amount_changed )
1065 hbox = QHBoxLayout()
1067 b = QPushButton(_("Save"))
1068 b.clicked.connect(do_save)
1070 b = QPushButton(_("Close"))
1072 b.clicked.connect(d.accept)
1074 vbox.addLayout(hbox)
1078 def question(self, msg):
1079 return QMessageBox.question(self, _('Message'), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
1081 def show_message(self, msg):
1082 QMessageBox.information(self, _('Message'), msg, _('OK'))
1084 def password_dialog(self ):
1091 vbox = QVBoxLayout()
1092 msg = _('Please enter your password')
1093 vbox.addWidget(QLabel(msg))
1095 grid = QGridLayout()
1097 grid.addWidget(QLabel(_('Password')), 1, 0)
1098 grid.addWidget(pw, 1, 1)
1099 vbox.addLayout(grid)
1101 vbox.addLayout(ok_cancel_buttons(d))
1104 if not d.exec_(): return
1105 return unicode(pw.text())
1112 def change_password_dialog( wallet, parent=None ):
1115 QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
1123 new_pw = QLineEdit()
1124 new_pw.setEchoMode(2)
1125 conf_pw = QLineEdit()
1126 conf_pw.setEchoMode(2)
1128 vbox = QVBoxLayout()
1130 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')
1132 msg = _("Please choose a password to encrypt your wallet keys.")+'\n'+_("Leave these fields empty if you want to disable encryption.")
1133 vbox.addWidget(QLabel(msg))
1135 grid = QGridLayout()
1138 if wallet.use_encryption:
1139 grid.addWidget(QLabel(_('Password')), 1, 0)
1140 grid.addWidget(pw, 1, 1)
1142 grid.addWidget(QLabel(_('New Password')), 2, 0)
1143 grid.addWidget(new_pw, 2, 1)
1145 grid.addWidget(QLabel(_('Confirm Password')), 3, 0)
1146 grid.addWidget(conf_pw, 3, 1)
1147 vbox.addLayout(grid)
1149 vbox.addLayout(ok_cancel_buttons(d))
1152 if not d.exec_(): return
1154 password = unicode(pw.text()) if wallet.use_encryption else None
1155 new_password = unicode(new_pw.text())
1156 new_password2 = unicode(conf_pw.text())
1159 seed = wallet.pw_decode( wallet.seed, password)
1161 QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
1164 if new_password != new_password2:
1165 QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
1168 wallet.update_password(seed, password, new_password)
1171 def seed_dialog(wallet, parent=None):
1175 vbox = QVBoxLayout()
1176 msg = _("Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet.")
1177 vbox.addWidget(QLabel(msg))
1179 grid = QGridLayout()
1182 seed_e = QLineEdit()
1183 grid.addWidget(QLabel(_('Seed or mnemonic')), 1, 0)
1184 grid.addWidget(seed_e, 1, 1)
1188 grid.addWidget(QLabel(_('Gap limit')), 2, 0)
1189 grid.addWidget(gap_e, 2, 1)
1190 gap_e.textChanged.connect(lambda: numbify(gap_e,True))
1191 vbox.addLayout(grid)
1193 vbox.addLayout(ok_cancel_buttons(d))
1196 if not d.exec_(): return
1199 gap = int(unicode(gap_e.text()))
1201 QMessageBox.warning(None, _('Error'), 'error', 'OK')
1205 seed = unicode(seed_e.text())
1208 print_error("Warning: Not hex, trying decode")
1210 seed = mnemonic.mn_decode( seed.split(' ') )
1212 QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK'))
1215 QMessageBox.warning(None, _('Error'), _('No seed'), 'OK')
1218 wallet.seed = str(seed)
1219 #print repr(wallet.seed)
1220 wallet.gap_limit = gap
1225 def settings_dialog(self):
1228 vbox = QVBoxLayout()
1229 msg = _('Here are the settings of your wallet.') + '\n'\
1230 + _('For more explanations, click on the help buttons next to each field.')
1233 label.setFixedWidth(250)
1234 label.setWordWrap(True)
1235 label.setAlignment(Qt.AlignJustify)
1236 vbox.addWidget(label)
1238 grid = QGridLayout()
1240 vbox.addLayout(grid)
1242 fee_label = QLabel(_('Transaction fee'))
1243 grid.addWidget(fee_label, 2, 0)
1245 fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) )
1246 grid.addWidget(fee_e, 2, 1)
1247 msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \
1248 + _('Recommended value') + ': 0.001'
1249 grid.addWidget(HelpButton(msg), 2, 2)
1250 fee_e.textChanged.connect(lambda: numbify(fee_e,False))
1251 if not self.config.is_modifiable('fee'):
1252 for w in [fee_e, fee_label]: w.setEnabled(False)
1254 nz_label = QLabel(_('Display zeros'))
1255 grid.addWidget(nz_label, 3, 0)
1257 nz_e.setText("%d"% self.wallet.num_zeros)
1258 grid.addWidget(nz_e, 3, 1)
1259 msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
1260 grid.addWidget(HelpButton(msg), 3, 2)
1261 nz_e.textChanged.connect(lambda: numbify(nz_e,True))
1262 if not self.config.is_modifiable('num_zeros'):
1263 for w in [nz_e, nz_label]: w.setEnabled(False)
1265 usechange_cb = QCheckBox(_('Use change addresses'))
1266 grid.addWidget(usechange_cb, 5, 0)
1267 usechange_cb.setChecked(self.wallet.use_change)
1268 grid.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions. ')), 5, 2)
1269 if not self.config.is_modifiable('use_change'): usechange_cb.setEnabled(False)
1271 gap_label = QLabel(_('Gap limit'))
1272 grid.addWidget(gap_label, 6, 0)
1274 gap_e.setText("%d"% self.wallet.gap_limit)
1275 grid.addWidget(gap_e, 6, 1)
1276 msg = _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \
1277 + _('You may increase it if you need more receiving addresses.') + '\n\n' \
1278 + _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \
1279 + _('Given the current status of your address sequence, the minimum gap limit you can use is: ') + '%d'%self.wallet.min_acceptable_gap() + '\n\n' \
1280 + _('Warning') + ': ' \
1281 + _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \
1282 + _('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'
1283 grid.addWidget(HelpButton(msg), 6, 2)
1284 gap_e.textChanged.connect(lambda: numbify(nz_e,True))
1285 if not self.config.is_modifiable('gap_limit'):
1286 for w in [gap_e, gap_label]: w.setEnabled(False)
1288 gui_label=QLabel(_('Default GUI') + ':')
1289 grid.addWidget(gui_label , 7, 0)
1290 gui_combo = QComboBox()
1291 gui_combo.addItems(['Lite', 'Qt', 'Gtk'])
1292 gui_combo.setCurrentIndex(gui_combo.findText(self.config.get("gui","lite").capitalize()))
1293 grid.addWidget(gui_combo, 7, 1)
1294 grid.addWidget(HelpButton(_('Select which GUI mode to use at start up. ')), 7, 2)
1295 if not self.config.is_modifiable('gui'):
1296 for w in [gui_combo, gui_label]: w.setEnabled(False)
1298 vbox.addLayout(ok_cancel_buttons(d))
1302 if not d.exec_(): return
1304 fee = unicode(fee_e.text())
1306 fee = int( 100000000 * Decimal(fee) )
1308 QMessageBox.warning(self, _('Error'), _('Invalid value') +': %s'%fee, _('OK'))
1311 if self.wallet.fee != fee:
1312 self.wallet.fee = fee
1315 nz = unicode(nz_e.text())
1320 QMessageBox.warning(self, _('Error'), _('Invalid value')+':%s'%nz, _('OK'))
1323 if self.wallet.num_zeros != nz:
1324 self.wallet.num_zeros = nz
1325 self.config.set_key('num_zeros', nz, True)
1326 self.update_history_tab()
1327 self.update_receive_tab()
1329 if self.wallet.use_change != usechange_cb.isChecked():
1330 self.wallet.use_change = usechange_cb.isChecked()
1331 self.config.set_key('use_change', self.wallet.use_change, True)
1334 n = int(gap_e.text())
1336 QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
1339 if self.wallet.gap_limit != n:
1340 r = self.wallet.change_gap_limit(n)
1342 self.update_receive_tab()
1343 self.config.set_key('gap_limit', self.wallet.gap_limit, True)
1345 QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
1347 self.config.set_key("gui", str(gui_combo.currentText()).lower(), True)
1352 def network_dialog(wallet, parent=None):
1353 interface = wallet.interface
1355 if interface.is_connected:
1356 status = _("Connected to")+" %s\n%d blocks"%(interface.host, wallet.blocks)
1358 status = _("Not connected")
1361 status = _("Please choose a server.")
1363 server = interface.server
1365 if not wallet.interface.servers:
1367 for x in DEFAULT_SERVERS:
1368 h,port,protocol = x.split(':')
1369 servers_list.append( (h,[(protocol,port)] ) )
1371 servers_list = wallet.interface.servers
1374 for item in servers_list:
1378 protocol, port = item2
1384 d.setWindowTitle(_('Server'))
1385 d.setMinimumSize(375, 20)
1387 vbox = QVBoxLayout()
1390 hbox = QHBoxLayout()
1392 l.setPixmap(QPixmap(":icons/network.png"))
1395 hbox.addWidget(QLabel(status))
1397 vbox.addLayout(hbox)
1401 grid = QGridLayout()
1403 vbox.addLayout(grid)
1406 server_protocol = QComboBox()
1407 server_host = QLineEdit()
1408 server_host.setFixedWidth(200)
1409 server_port = QLineEdit()
1410 server_port.setFixedWidth(60)
1411 server_protocol.addItems(['TCP', 'HTTP'])
1413 host, port, protocol = server.split(':')
1414 server_host.setText(host)
1415 server_port.setText(port)
1416 server_protocol.setCurrentIndex(0 if protocol=='t' else 1)
1418 grid.addWidget(QLabel(_('Server') + ':'), 0, 0)
1419 grid.addWidget(server_protocol, 0, 1)
1420 grid.addWidget(server_host, 0, 2)
1421 grid.addWidget(server_port, 0, 3)
1423 def change_protocol(p):
1424 protocol = 't' if p == 0 else 'h'
1425 host = unicode(server_host.text())
1427 if protocol not in pp.keys():
1428 protocol = pp.keys()[0]
1430 server_host.setText( host )
1431 server_port.setText( port )
1433 server_protocol.connect(server_protocol, SIGNAL('currentIndexChanged(int)'), change_protocol)
1436 if wallet.interface.servers:
1437 label = _('Active Servers')
1439 label = _('Default Servers')
1441 servers_list_widget = QTreeWidget(parent)
1442 servers_list_widget.setHeaderLabels( [ label ] )
1443 servers_list_widget.setMaximumHeight(150)
1444 for host in plist.keys():
1445 servers_list_widget.addTopLevelItem(QTreeWidgetItem( [ host ] ))
1447 def change_server(x):
1448 host = unicode(x.text(0))
1450 if 't' in pp.keys():
1453 protocol = pp.keys()[0]
1455 server_host.setText( host )
1456 server_port.setText( port )
1457 server_protocol.setCurrentIndex(0 if protocol == 't' else 1)
1459 servers_list_widget.connect(servers_list_widget, SIGNAL('itemClicked(QTreeWidgetItem*, int)'), change_server)
1460 grid.addWidget(servers_list_widget, 1, 1, 1, 3)
1462 if not wallet.config.is_modifiable('server'):
1463 for w in [server_host, server_port, server_protocol, servers_list_widget]: w.setEnabled(False)
1466 proxy_mode = QComboBox()
1467 proxy_host = QLineEdit()
1468 proxy_host.setFixedWidth(200)
1469 proxy_port = QLineEdit()
1470 proxy_port.setFixedWidth(60)
1471 proxy_mode.addItems(['NONE', 'SOCKS4', 'SOCKS5', 'HTTP'])
1473 def check_for_disable(index = False):
1474 if proxy_mode.currentText() != 'NONE':
1475 proxy_host.setEnabled(True)
1476 proxy_port.setEnabled(True)
1478 proxy_host.setEnabled(False)
1479 proxy_port.setEnabled(False)
1482 proxy_mode.connect(proxy_mode, SIGNAL('currentIndexChanged(int)'), check_for_disable)
1484 if not wallet.config.is_modifiable('proxy'):
1485 for w in [proxy_host, proxy_port, proxy_mode]: w.setEnabled(False)
1487 proxy_config = interface.proxy if interface.proxy else { "mode":"none", "host":"localhost", "port":"8080"}
1488 proxy_mode.setCurrentIndex(proxy_mode.findText(str(proxy_config.get("mode").upper())))
1489 proxy_host.setText(proxy_config.get("host"))
1490 proxy_port.setText(proxy_config.get("port"))
1492 grid.addWidget(QLabel(_('Proxy') + ':'), 2, 0)
1493 grid.addWidget(proxy_mode, 2, 1)
1494 grid.addWidget(proxy_host, 2, 2)
1495 grid.addWidget(proxy_port, 2, 3)
1498 vbox.addLayout(ok_cancel_buttons(d))
1501 if not d.exec_(): return
1503 server = unicode( server_host.text() ) + ':' + unicode( server_port.text() ) + ':' + ('t' if server_protocol.currentIndex() == 0 else 'h')
1504 if proxy_mode.currentText() != 'NONE':
1505 proxy = { u'mode':unicode(proxy_mode.currentText()).lower(), u'host':unicode(proxy_host.text()), u'port':unicode(proxy_port.text()) }
1509 wallet.config.set_key("proxy", proxy, True)
1510 wallet.config.set_key("server", server, True)
1511 interface.set_server(server, proxy)
1515 def closeEvent(self, event):
1517 self.config.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()], True)
1523 def __init__(self, wallet, config, app=None):
1524 self.wallet = wallet
1525 self.config = config
1527 self.app = QApplication(sys.argv)
1529 def server_list_changed(self):
1533 def restore_or_create(self):
1535 msg = _("Wallet file not found.")+"\n"+_("Do you want to create a new wallet, or to restore an existing one?")
1536 r = QMessageBox.question(None, _('Message'), msg, _('Create'), _('Restore'), _('Cancel'), 0, 2)
1537 if r==2: return False
1539 is_recovery = (r==1)
1540 wallet = self.wallet
1541 # ask for the server.
1542 if not ElectrumWindow.network_dialog( wallet, parent=None ): return False
1544 waiting = lambda: False if wallet.up_to_date else "Please wait...\nAddresses generated: %d\nKilobytes received: %.1f"\
1545 %(len(wallet.all_addresses()), wallet.interface.bytes_received/1024.)
1548 wallet.new_seed(None)
1549 wallet.init_mpk( wallet.seed )
1550 wallet.up_to_date_event.clear()
1551 wallet.up_to_date = False
1552 wallet.interface.poke()
1553 waiting_dialog(waiting)
1554 # run a dialog indicating the seed, ask the user to remember it
1555 ElectrumWindow.show_seed_dialog(wallet)
1557 ElectrumWindow.change_password_dialog(wallet)
1559 # ask for seed and gap.
1560 if not ElectrumWindow.seed_dialog( wallet ): return False
1561 wallet.init_mpk( wallet.seed )
1562 wallet.up_to_date_event.clear()
1563 wallet.up_to_date = False
1564 wallet.interface.poke()
1565 waiting_dialog(waiting)
1566 if wallet.is_found():
1567 # history and addressbook
1568 wallet.update_tx_history()
1569 wallet.fill_addressbook()
1570 print "Recovery successful"
1573 QMessageBox.information(None, _('Error'), _("No transactions found for this seed"), _('OK'))
1581 w = ElectrumWindow(self.wallet, self.config)
1582 if url: w.set_url(url)