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
23 from PyQt4.QtGui import *
24 from PyQt4.QtCore import *
25 import PyQt4.QtCore as QtCore
26 import PyQt4.QtGui as QtGui
31 print "Could not import icons_rp.py"
32 print "Please generate it with: 'pyrcc4 icons.qrc -o icons_rc.py'"
35 from wallet import format_satoshis
36 from decimal import Decimal
39 def numbify(entry, is_int = False):
40 text = unicode(entry.text()).strip()
42 if not is_int: chars +='.'
43 s = ''.join([i for i in text if i in chars])
48 s = s[:p] + '.' + s[p:p+8]
50 amount = int( Decimal(s) * 100000000 )
62 class Timer(QtCore.QThread):
65 self.emit(QtCore.SIGNAL('timersignal'))
68 class EnterButton(QPushButton):
69 def __init__(self, text, func):
70 QPushButton.__init__(self, text)
72 self.clicked.connect(func)
74 def keyPressEvent(self, e):
75 if e.key() == QtCore.Qt.Key_Return:
78 class StatusBarButton(QPushButton):
79 def __init__(self, icon, tooltip, func):
80 QPushButton.__init__(self, icon, '')
81 self.setToolTip(tooltip)
83 self.setMaximumWidth(25)
84 self.clicked.connect(func)
87 def keyPressEvent(self, e):
88 if e.key() == QtCore.Qt.Key_Return:
92 class QRCodeWidget(QWidget):
94 def __init__(self, addr):
96 super(QRCodeWidget, self).__init__()
98 self.setGeometry(300, 300, 350, 350)
99 self.setWindowTitle('Colors')
101 self.qr = pyqrnative.QRCode(4, pyqrnative.QRErrorCorrectLevel.H)
102 self.qr.addData(addr)
105 def paintEvent(self, e):
106 qp = QtGui.QPainter()
109 size = self.qr.getModuleCount()*boxsize
110 k = self.qr.getModuleCount()
111 black = QColor(0, 0, 0, 255)
112 white = QColor(255, 255, 255, 255)
115 if self.qr.isDark(r, c):
121 qp.drawRect(c*boxsize, r*boxsize, boxsize, boxsize)
126 def ok_cancel_buttons(dialog):
129 b = QPushButton("OK")
131 b.clicked.connect(dialog.accept)
132 b = QPushButton("Cancel")
134 b.clicked.connect(dialog.reject)
138 class ElectrumWindow(QMainWindow):
140 def __init__(self, wallet):
141 QMainWindow.__init__(self)
143 self.funds_error = False
145 self.tabs = tabs = QTabWidget(self)
146 tabs.addTab(self.create_history_tab(), 'History')
147 tabs.addTab(self.create_send_tab(), 'Send')
148 tabs.addTab(self.create_receive_tab(), 'Receive')
149 tabs.addTab(self.create_contacts_tab(),'Contacts')
150 tabs.addTab(self.create_wall_tab(), 'Wall')
151 tabs.setMinimumSize(600, 400)
152 tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
153 self.setCentralWidget(tabs)
154 self.create_status_bar()
155 self.setGeometry(100,100,840,400)
156 self.setWindowTitle( 'Electrum ' + self.wallet.electrum_version )
159 QShortcut(QKeySequence("Ctrl+W"), self, self.close)
160 QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
161 QShortcut(QKeySequence("Ctrl+PgUp"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() - 1 )%tabs.count() ))
162 QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() + 1 )%tabs.count() ))
166 def connect_slots(self, sender):
167 self.connect(sender, QtCore.SIGNAL('timersignal'), self.update_wallet)
168 self.connect(sender, QtCore.SIGNAL('timersignal'), self.check_recipient)
169 self.previous_payto_e=''
171 def check_recipient(self):
172 if self.payto_e.hasFocus():
174 r = unicode( self.payto_e.text() )
175 if r != self.previous_payto_e:
176 self.previous_payto_e = r
178 if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
180 to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
184 s = r + ' <' + to_address + '>'
185 self.payto_e.setText(s)
188 def update_wallet(self):
189 if self.wallet.interface.is_connected:
190 if self.wallet.blocks == 0:
191 text = "Server not ready"
192 icon = QIcon(":icons/status_disconnected.png")
193 elif not self.wallet.up_to_date:
194 text = "Synchronizing..."
195 icon = QIcon(":icons/status_waiting.png")
197 c, u = self.wallet.get_balance()
198 text = "Balance: %s "%( format_satoshis(c) )
199 if u: text += "[%s unconfirmed]"%( format_satoshis(u,True) )
200 icon = QIcon(":icons/status_connected.png")
202 text = "Not connected"
203 icon = QIcon(":icons/status_disconnected.png")
206 text = "Not enough funds"
208 self.statusBar().showMessage(text)
209 self.status_button.setIcon( icon )
211 if self.wallet.was_updated and self.wallet.up_to_date:
212 self.wallet.was_updated = False
213 self.textbox.setText( self.wallet.banner )
214 self.update_history_tab()
215 self.update_receive_tab()
216 self.update_contacts_tab()
219 def create_history_tab(self):
220 self.history_list = w = QTreeWidget(self)
221 #print w.getContentsMargins()
223 w.setColumnWidth(0, 40)
224 w.setColumnWidth(1, 140)
225 w.setColumnWidth(2, 350)
226 w.setColumnWidth(3, 140)
227 w.setColumnWidth(4, 140)
228 w.setHeaderLabels( [ '', 'Date', 'Description', 'Amount', 'Balance'] )
229 self.connect(w, SIGNAL('itemActivated(QTreeWidgetItem*, int)'), self.tx_details)
230 self.connect(w, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.tx_label_clicked)
231 self.connect(w, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), self.tx_label_changed)
234 def tx_details(self, item, column):
235 tx_hash = str(item.toolTip(0))
236 tx = self.wallet.tx_history.get(tx_hash)
239 conf = self.wallet.blocks - tx['height'] + 1
240 time_str = datetime.datetime.fromtimestamp( tx['nTime']).isoformat(' ')[:-3]
245 tx_details = "Transaction Details:\n\n" \
246 + "Transaction ID:\n" + tx_hash + "\n\n" \
247 + "Status: %d confirmations\n\n"%conf \
248 + "Date: %s\n\n"%time_str \
249 + "Inputs:\n-"+ '\n-'.join(tx['inputs']) + "\n\n" \
250 + "Outputs:\n-"+ '\n-'.join(tx['outputs'])
252 r = self.wallet.receipts.get(tx_hash)
254 tx_details += "\n_______________________________________" \
255 + '\n\nSigned URI: ' + r[2] \
256 + "\n\nSigned by: " + r[0] \
257 + '\n\nSignature: ' + r[1]
259 QMessageBox.information(self, 'Details', tx_details, 'OK')
262 def tx_label_clicked(self, item, column):
263 if column==2 and item.isSelected():
264 tx_hash = str(item.toolTip(0))
266 #if not self.wallet.labels.get(tx_hash): item.setText(2,'')
267 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
268 self.history_list.editItem( item, column )
269 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
272 def tx_label_changed(self, item, column):
276 tx_hash = str(item.toolTip(0))
277 tx = self.wallet.tx_history.get(tx_hash)
278 s = self.wallet.labels.get(tx_hash)
279 text = unicode( item.text(2) )
281 self.wallet.labels[tx_hash] = text
282 item.setForeground(2, QBrush(QColor('black')))
284 if s: self.wallet.labels.pop(tx_hash)
285 text = tx['default_label']
286 item.setText(2, text)
287 item.setForeground(2, QBrush(QColor('gray')))
290 def address_label_clicked(self, item, column, l):
291 if column==1 and item.isSelected():
292 item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
293 l.editItem( item, column )
294 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
296 def address_label_changed(self, item, column, l):
297 addr = unicode( item.text(0) )
298 text = unicode( item.text(1) )
300 self.wallet.labels[addr] = text
302 s = self.wallet.labels.get(addr)
303 if s: self.wallet.labels.pop(addr)
304 self.update_history_tab()
306 def update_history_tab(self):
307 self.history_list.clear()
309 for tx in self.wallet.get_tx_history():
310 tx_hash = tx['tx_hash']
312 conf = self.wallet.blocks - tx['height'] + 1
313 time_str = datetime.datetime.fromtimestamp( tx['nTime']).isoformat(' ')[:-3]
314 icon = QIcon(":icons/confirmed.png")
318 icon = QIcon(":icons/unconfirmed.png")
321 label = self.wallet.labels.get(tx_hash)
322 is_default_label = (label == '') or (label is None)
323 if is_default_label: label = tx['default_label']
325 item = QTreeWidgetItem( [ '', time_str, label, format_satoshis(v,True), format_satoshis(balance)] )
326 item.setFont(2, QFont('monospace'))
327 item.setFont(3, QFont('monospace'))
328 item.setFont(4, QFont('monospace'))
329 item.setToolTip(0, tx_hash)
331 item.setForeground(2, QBrush(QColor('grey')))
333 item.setIcon(0, icon)
334 self.history_list.insertTopLevelItem(0,item)
337 def create_send_tab(self):
342 grid.setColumnMinimumWidth(3,300)
343 grid.setColumnStretch(4,1)
345 self.payto_e = QLineEdit()
346 grid.addWidget(QLabel('Pay to'), 1, 0)
347 grid.addWidget(self.payto_e, 1, 1, 1, 3)
349 self.message_e = QLineEdit()
350 grid.addWidget(QLabel('Description'), 2, 0)
351 grid.addWidget(self.message_e, 2, 1, 1, 3)
353 self.amount_e = QLineEdit()
354 grid.addWidget(QLabel('Amount'), 3, 0)
355 grid.addWidget(self.amount_e, 3, 1, 1, 2)
357 self.fee_e = QLineEdit()
358 grid.addWidget(QLabel('Fee'), 4, 0)
359 grid.addWidget(self.fee_e, 4, 1, 1, 2)
361 b = EnterButton("Send", self.do_send)
362 grid.addWidget(b, 5, 1)
364 b = EnterButton("Clear",self.do_clear)
365 grid.addWidget(b, 5, 2)
367 self.payto_sig = QLabel('')
368 grid.addWidget(self.payto_sig, 6, 0, 1, 4)
379 def entry_changed( is_fee ):
380 self.funds_error = False
381 amount = numbify(self.amount_e)
382 fee = numbify(self.fee_e)
383 if not is_fee: fee = None
386 inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee )
388 self.fee_e.setText( str( Decimal( fee ) / 100000000 ) )
391 palette.setColor(self.amount_e.foregroundRole(), QColor('black'))
394 palette.setColor(self.amount_e.foregroundRole(), QColor('red'))
395 self.funds_error = True
396 self.amount_e.setPalette(palette)
397 self.fee_e.setPalette(palette)
399 self.amount_e.textChanged.connect(lambda: entry_changed(False) )
400 self.fee_e.textChanged.connect(lambda: entry_changed(True) )
406 label = unicode( self.message_e.text() )
407 r = unicode( self.payto_e.text() )
410 m1 = re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r)
411 m2 = re.match('(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+) \<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
414 to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
418 to_address = m2.group(5)
422 if not self.wallet.is_valid(to_address):
423 QMessageBox.warning(self, 'Error', 'Invalid Bitcoin Address:\n'+to_address, 'OK')
427 amount = int( Decimal( unicode( self.amount_e.text())) * 100000000 )
429 QMessageBox.warning(self, 'Error', 'Invalid Amount', 'OK')
432 fee = int( Decimal( unicode( self.fee_e.text())) * 100000000 )
434 QMessageBox.warning(self, 'Error', 'Invalid Fee', 'OK')
437 if self.wallet.use_encryption:
438 password = self.password_dialog()
445 tx = self.wallet.mktx( to_address, amount, label, password, fee )
446 except BaseException, e:
447 self.show_message(e.message)
450 status, msg = self.wallet.sendtx( tx )
452 QMessageBox.information(self, '', 'Payment sent.\n'+msg, 'OK')
454 self.update_contacts_tab()
456 QMessageBox.warning(self, 'Error', msg, 'OK')
459 def set_url(self, url):
460 payto, amount, label, message, signature, identity, url = self.wallet.parse_url(url, self.show_message, self.question)
461 self.tabs.setCurrentIndex(1)
462 self.payto_e.setText(payto)
463 self.message_e.setText(message)
464 self.amount_e.setText(amount)
466 self.set_frozen(self.payto_e,True)
467 self.set_frozen(self.amount_e,True)
468 self.set_frozen(self.message_e,True)
469 self.payto_sig.setText( ' The bitcoin URI was signed by ' + identity )
471 self.payto_sig.setVisible(False)
474 self.payto_sig.setVisible(False)
475 for e in [self.payto_e, self.message_e, self.amount_e, self.fee_e]:
477 self.set_frozen(e,False)
479 def set_frozen(self,entry,frozen):
481 entry.setReadOnly(True)
482 entry.setFrame(False)
484 palette.setColor(entry.backgroundRole(), QColor('lightgray'))
485 entry.setPalette(palette)
487 entry.setReadOnly(False)
490 palette.setColor(entry.backgroundRole(), QColor('white'))
491 entry.setPalette(palette)
494 def make_address_list(self, is_recv):
496 l = QTreeWidget(self)
498 l.setColumnWidth(0, 350)
499 l.setColumnWidth(1, 330)
500 l.setColumnWidth(2, 20)
501 l.setHeaderLabels( ['Address', 'Label','Tx'])
515 addr = unicode( i.text(0) )
518 def showqrcode(address):
519 if not address: return
522 d.setMinimumSize(270, 300)
524 vbox.addWidget(QRCodeWidget(address))
525 vbox.addLayout(ok_cancel_buttons(d))
528 qrButton = EnterButton("QR",lambda: showqrcode(get_addr(l)))
530 def copy2clipboard(addr):
531 self.app.clipboard().setText(addr)
532 copyButton = EnterButton("Copy to Clipboard", lambda: copy2clipboard(get_addr(l)))
533 hbox.addWidget(qrButton)
534 hbox.addWidget(copyButton)
536 addButton = EnterButton("New", self.newaddress_dialog)
537 hbox.addWidget(addButton)
540 self.tabs.setCurrentIndex(1)
541 self.payto_e.setText(addr)
542 self.amount_e.setFocus()
543 paytoButton = EnterButton('Pay to', lambda: payto(get_addr(l)))
544 hbox.addWidget(paytoButton)
547 buttons.setLayout(hbox)
548 vbox.addWidget(buttons)
554 def create_receive_tab(self):
555 w, l = self.make_address_list(True)
556 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l))
557 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l))
558 self.receive_list = l
561 def create_contacts_tab(self):
562 w, l = self.make_address_list(False)
563 self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l))
564 self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l))
565 self.connect(l, SIGNAL('itemActivated(QTreeWidgetItem*, int)'), self.show_contact_details)
566 self.contacts_list = l
569 def update_receive_tab(self):
570 self.receive_list.clear()
571 for address in self.wallet.all_addresses():
572 if self.wallet.is_change(address):continue
573 label = self.wallet.labels.get(address,'')
575 h = self.wallet.history.get(address,[])
577 if not item['is_in'] : n=n+1
578 tx = "None" if n==0 else "%d"%n
579 item = QTreeWidgetItem( [ address, label, tx] )
580 item.setFont(0, QFont('monospace'))
581 self.receive_list.addTopLevelItem(item)
583 def show_contact_details(self, item, column):
584 m = unicode(item.text(0))
585 a = self.wallet.aliases.get(m)
587 if a[0] in self.wallet.authorities.keys():
588 s = self.wallet.authorities.get(a[0])
591 msg = 'Alias: '+ m + '\nTarget address: '+ a[1] + '\n\nSigned by: ' + s + '\nSigning address:' + a[0]
592 QMessageBox.information(self, 'Alias', msg, 'OK')
594 def update_contacts_tab(self):
595 self.contacts_list.clear()
596 for alias, v in self.wallet.aliases.items():
598 label = self.wallet.labels.get(alias,'')
599 item = QTreeWidgetItem( [ alias, label, '-'] )
600 self.contacts_list.addTopLevelItem(item)
602 for address in self.wallet.addressbook:
603 label = self.wallet.labels.get(address,'')
605 for item in self.wallet.tx_history.values():
606 if address in item['outputs'] : n=n+1
607 tx = "None" if n==0 else "%d"%n
608 item = QTreeWidgetItem( [ address, label, tx] )
609 item.setFont(0, QFont('monospace'))
610 self.contacts_list.addTopLevelItem(item)
613 def create_wall_tab(self):
614 self.textbox = textbox = QTextEdit(self)
615 textbox.setFont(QFont('monospace'))
616 textbox.setReadOnly(True)
619 def create_status_bar(self):
621 sb.setFixedHeight(35)
622 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), "Password", lambda: self.change_password_dialog(self.wallet, self) ) )
623 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), "Preferences", self.settings_dialog ) )
624 sb.addPermanentWidget( StatusBarButton( QIcon(":icons/seed.png"), "Seed", lambda: self.show_seed_dialog(self.wallet, self) ) )
625 self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), "Network", lambda: self.network_dialog(self.wallet, self) )
626 sb.addPermanentWidget( self.status_button )
627 self.setStatusBar(sb)
629 def newaddress_dialog(self):
630 text, ok = QInputDialog.getText(self, 'New Contact', 'Address:')
631 address = unicode(text)
633 if self.wallet.is_valid(address):
634 self.wallet.addressbook.append(address)
636 self.update_contacts_tab()
638 QMessageBox.warning(self, 'Error', 'Invalid Address', 'OK')
641 def show_seed_dialog(wallet, parent=None):
643 if wallet.use_encryption:
644 password = parent.password_dialog()
645 if not password: return
650 seed = wallet.pw_decode( wallet.seed, password)
652 QMessageBox.warning(parent, 'Error', 'Invalid Password', 'OK')
655 msg = "Your wallet generation seed is:\n\n" + seed \
656 + "\n\nPlease keep it in a safe place; if you lose it, you will not be able to restore your wallet.\n\n" \
657 + "Equivalently, your wallet seed can be stored and recovered with the following mnemonic code:\n\n\"" \
658 + ' '.join(mnemonic.mn_encode(seed)) + "\""
660 QMessageBox.information(parent, 'Seed', msg, 'OK')
662 def question(self, msg):
663 return QMessageBox.question(self, 'Message', msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
665 def show_message(self, msg):
666 QMessageBox.information(self, 'Message', msg, 'OK')
668 def password_dialog(self ):
676 msg = 'Please enter your password'
677 vbox.addWidget(QLabel(msg))
681 grid.addWidget(QLabel('Password'), 1, 0)
682 grid.addWidget(pw, 1, 1)
685 vbox.addLayout(ok_cancel_buttons(d))
688 if not d.exec_(): return
689 return unicode(pw.text())
692 def change_password_dialog( wallet, parent=None ):
699 new_pw.setEchoMode(2)
700 conf_pw = QLineEdit()
701 conf_pw.setEchoMode(2)
705 msg = 'Your wallet is encrypted. Use this dialog to change your password.\nTo disable wallet encryption, enter an empty new password.' if wallet.use_encryption else 'Your wallet keys are not encrypted'
707 msg = "Please choose a password to encrypt your wallet keys.\nLeave these fields empty if you want to disable encryption."
708 vbox.addWidget(QLabel(msg))
713 if wallet.use_encryption:
714 grid.addWidget(QLabel('Password'), 1, 0)
715 grid.addWidget(pw, 1, 1)
717 grid.addWidget(QLabel('New Password'), 2, 0)
718 grid.addWidget(new_pw, 2, 1)
720 grid.addWidget(QLabel('Confirm Password'), 3, 0)
721 grid.addWidget(conf_pw, 3, 1)
724 vbox.addLayout(ok_cancel_buttons(d))
727 if not d.exec_(): return
729 password = unicode(pw.text()) if wallet.use_encryption else None
730 new_password = unicode(new_pw.text())
731 new_password2 = unicode(conf_pw.text())
734 seed = wallet.pw_decode( wallet.seed, password)
736 QMessageBox.warning(parent, 'Error', 'Incorrect Password', 'OK')
739 if new_password != new_password2:
740 QMessageBox.warning(parent, 'Error', 'Passwords do not match', 'OK')
743 wallet.update_password(seed, new_password)
746 def seed_dialog(wallet, parent=None):
751 msg = "Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet."
752 vbox.addWidget(QLabel(msg))
758 grid.addWidget(QLabel('Seed or mnemonic'), 1, 0)
759 grid.addWidget(seed_e, 1, 1)
763 grid.addWidget(QLabel('Gap limit'), 2, 0)
764 grid.addWidget(gap_e, 2, 1)
765 gap_e.textChanged.connect(lambda: numbify(gap_e,True))
768 vbox.addLayout(ok_cancel_buttons(d))
771 if not d.exec_(): return
774 gap = int(unicode(gap_e.text()))
776 QMessageBox.warning(None, 'Error', 'error', 'OK')
780 seed = unicode(seed_e.text())
784 print "not hex, trying decode"
786 seed = mnemonic.mn_decode( seed.split(' ') )
788 QMessageBox.warning(None, 'Error', 'I cannot decode this', 'OK')
791 QMessageBox.warning(None, 'Error', 'no seed', 'OK')
794 wallet.seed = str(seed)
795 #print repr(wallet.seed)
796 wallet.gap_limit = gap
800 def settings_dialog(self):
806 msg = 'Here are the settings of your wallet.'
807 vbox.addWidget(QLabel(msg))
813 fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) )
814 grid.addWidget(QLabel('Fee per tx. input'), 2, 0)
815 grid.addWidget(fee_e, 2, 1)
817 fee_e.textChanged.connect(lambda: numbify(fee_e,False))
819 vbox.addLayout(ok_cancel_buttons(d))
822 if not d.exec_(): return
824 fee = unicode(fee_e.text())
826 fee = int( 100000000 * Decimal(fee) )
828 QMessageBox.warning(self, 'Error', 'Invalid value:%s'%fee, 'OK')
831 self.wallet.fee = fee
835 def network_dialog(wallet, parent=None):
836 interface = wallet.interface
838 if interface.is_connected:
839 status = "Connected to %s:%d\n%d blocks\nresponse time: %f"%(interface.host, interface.port, wallet.blocks, interface.rtime)
841 status = "Not connected"
846 status = "Please choose a server."
847 host = random.choice( interface.servers )
852 d.setWindowTitle('Server')
853 d.setMinimumSize(375, 20)
860 l.setPixmap(QPixmap(":icons/network.png"))
862 hbox.addWidget(QLabel(status))
867 host_line = QLineEdit()
868 host_line.setText("%s:%d"% (host,port) )
869 hbox.addWidget(QLabel('Connect to:'))
870 hbox.addWidget(host_line)
873 if wallet.interface.servers:
874 servers_list = QTreeWidget(parent)
875 servers_list.setHeaderLabels( [ 'Active servers'] )
876 servers_list.setMaximumHeight(150)
877 for item in wallet.interface.servers:
878 servers_list.addTopLevelItem(QTreeWidgetItem( [ item ] ))
879 servers_list.connect(servers_list, SIGNAL('itemClicked(QTreeWidgetItem*, int)'), lambda x:host_line.setText( x.text(0) + ':%d'%wallet.port ))
880 vbox.addWidget(servers_list)
883 hbox.addWidget(QLabel('No nodes available'))
884 b = EnterButton("Find nodes", lambda: wallet.interface.get_servers(wallet) )
888 vbox.addLayout(ok_cancel_buttons(d))
891 if not d.exec_(): return
892 hh = unicode( host_line.text() )
896 host, port = hh.split(':')
902 QMessageBox.information(None, 'Error', 'error', 'OK')
908 wallet.set_server(host, port)
915 def __init__(self, wallet):
917 self.app = QApplication(sys.argv)
919 def restore_or_create(self):
921 msg = "Wallet file not found.\nDo you want to create a new wallet,\n or to restore an existing one?"
922 r = QMessageBox.question(None, 'Message', msg, 'create', 'restore', 'cancel', 0, 2)
923 if r==2: return False
927 # ask for the server.
928 if not ElectrumWindow.network_dialog( wallet, parent=None ): return False
932 wallet.new_seed(None)
933 wallet.init_mpk( wallet.seed )
934 wallet.up_to_date_event.clear()
936 # run a dialog indicating the seed, ask the user to remember it
937 ElectrumWindow.show_seed_dialog(wallet)
939 ElectrumWindow.change_password_dialog(wallet)
941 # ask for seed and gap.
942 if not ElectrumWindow.seed_dialog( wallet ): return False
943 wallet.init_mpk( wallet.seed )
944 wallet.up_to_date_event.clear()
947 if wallet.is_found():
948 # history and addressbook
949 wallet.update_tx_history()
950 wallet.fill_addressbook()
951 print "recovery successful"
954 QMessageBox.information(None, 'Message', "No transactions found for this seed", 'OK')
962 w = ElectrumWindow(self.wallet)
963 if url: w.set_url(url)