set_language(config.get('language'))
self.funds_error = False
+ self.payment_request = None
self.completions = QStringListModel()
self.tabs = tabs = QTabWidget(self)
self.connect(self, QtCore.SIGNAL('transaction_signal'), lambda: self.notify_transactions() )
self.connect(self, QtCore.SIGNAL('send_tx2'), self.send_tx2)
self.connect(self, QtCore.SIGNAL('send_tx3'), self.send_tx3)
+ self.connect(self, QtCore.SIGNAL('payment_request_ok'), self.payment_request_ok)
self.history_list.setFocus(True)
self.password_menu.setEnabled(not self.wallet.is_watching_only())
self.seed_menu.setEnabled(self.wallet.has_seed())
self.mpk_menu.setEnabled(self.wallet.is_deterministic())
+ self.import_menu.setEnabled(self.wallet.can_import())
self.update_lock_icon()
self.update_buttons_on_seed()
self.private_keys_menu = wallet_menu.addMenu(_("&Private keys"))
self.private_keys_menu.addAction(_("&Sweep"), self.sweep_key_dialog)
- self.private_keys_menu.addAction(_("&Import"), self.do_import_privkey)
+ self.import_menu = self.private_keys_menu.addAction(_("&Import"), self.do_import_privkey)
self.private_keys_menu.addAction(_("&Export"), self.export_privkeys_dialog)
-
- wallet_menu.addAction(_("&Export History"), self.do_export_history)
+ wallet_menu.addAction(_("&Export History"), self.export_history_dialog)
tools_menu = menubar.addMenu(_("&Tools"))
def do_send(self):
-
label = unicode( self.message_e.text() )
- r = unicode( self.payto_e.text() )
- r = r.strip()
- # label or alias, with address in brackets
- m = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
- to_address = m.group(2) if m else r
+ if self.payment_request:
+ outputs = self.payment_request.outputs
+ amount = self.payment_request.get_amount()
- if not is_valid(to_address):
- QMessageBox.warning(self, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK'))
- return
+ else:
+ r = unicode( self.payto_e.text() )
+ r = r.strip()
+
+ # label or alias, with address in brackets
+ m = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
+ to_address = m.group(2) if m else r
+ if not is_valid(to_address):
+ QMessageBox.warning(self, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK'))
+ return
+
+ try:
+ amount = self.read_amount(unicode( self.amount_e.text()))
+ except Exception:
+ QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
+ return
+
+ outputs = [(to_address, amount)]
- try:
- amount = self.read_amount(unicode( self.amount_e.text()))
- except Exception:
- QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
- return
try:
fee = self.read_amount(unicode( self.fee_e.text()))
except Exception:
if not self.question(_("The fee for this transaction seems unusually high.\nAre you really sure you want to pay %(fee)s in fees?")%{ 'fee' : self.format_amount(fee) + ' '+ self.base_unit()}):
return
- self.send_tx(to_address, amount, fee, label)
+ self.send_tx(outputs, fee, label)
def waiting_dialog(self, message):
@protected
- def send_tx(self, to_address, amount, fee, label, password):
+ def send_tx(self, outputs, fee, label, password):
# first, create an unsigned tx
domain = self.get_payment_sources()
- outputs = [(to_address, amount)]
try:
tx = self.wallet.make_unsigned_transaction(outputs, fee, None, domain)
tx.error = None
self.tx_wait_dialog = self.waiting_dialog('Signing..')
threading.Thread(target=sign_thread).start()
- # add recipient to addressbook
- if to_address not in self.wallet.addressbook and not self.wallet.is_mine(to_address):
- self.wallet.addressbook.append(to_address)
def send_tx2(self):
self.show_transaction(tx)
return
- # broadcast the tx
def broadcast_thread():
+ if self.payment_request:
+ refund_address = self.wallet.addresses()[0]
+ self.payment_request.send_ack(str(tx), refund_address)
+ self.payment_request = None
+ # note: BIP 70 recommends not broadcasting the tx to the network and letting the merchant do that
self.tx_broadcast_result = self.wallet.sendtx(tx)
self.emit(SIGNAL('send_tx3'))
+
self.tx_broadcast_dialog = self.waiting_dialog('Broadcasting..')
threading.Thread(target=broadcast_thread).start()
+
def send_tx3(self):
self.tx_broadcast_dialog.accept()
status, msg = self.tx_broadcast_result
if status:
QMessageBox.information(self, '', _('Payment sent.') + '\n' + msg, _('OK'))
self.do_clear()
- self.update_contacts_tab()
else:
QMessageBox.warning(self, _('Error'), msg, _('OK'))
-
-
+ def payment_request_ok(self):
+ self.payto_e.setText(self.payment_request.domain)
+ self.payto_e.setReadOnly(True)
+ self.amount_e.setText(self.format_amount(self.payment_request.get_amount()))
+ self.amount_e.setReadOnly(True)
def set_send(self, address, amount, label, message):
e.setReadOnly(True)
vbox.addWidget(e)
- hbox = QHBoxLayout()
- vbox.addLayout(hbox)
-
defaultname = 'electrum-private-keys.csv'
- directory = self.config.get('io_dir', unicode(os.path.expanduser('~')))
- path = os.path.join( directory, defaultname )
- filename_e = QLineEdit()
- filename_e.setText(path)
- def func():
- select_export = _('Select file to export your private keys to')
- p = self.getSaveFileName(select_export, defaultname, "*.csv")
- if p:
- filename_e.setText(p)
-
- button = QPushButton(_('File'))
- button.clicked.connect(func)
- hbox.addWidget(button)
- hbox.addWidget(filename_e)
+ select_msg = _('Select file to export your private keys to')
+ hbox, filename_e, csv_button = filename_field(self, self.config, defaultname, select_msg)
+ vbox.addLayout(hbox)
h, b = ok_cancel_buttons2(d, _('Export'))
b.setEnabled(False)
private_keys = {}
addresses = self.wallet.addresses(True)
+ done = False
def privkeys_thread():
for addr in addresses:
time.sleep(0.1)
+ if done:
+ break
private_keys[addr] = "\n".join(self.wallet.get_private_key(addr, password))
d.emit(SIGNAL('computing_privkeys'))
d.emit(SIGNAL('show_privkeys'))
threading.Thread(target=privkeys_thread).start()
if not d.exec_():
+ done = True
return
filename = filename_e.text()
return
try:
- self.do_export_privkeys(filename, private_keys)
+ self.do_export_privkeys(filename, private_keys, csv_button.isChecked())
except (IOError, os.error), reason:
export_error_label = _("Electrum was unable to produce a private key-export.")
QMessageBox.critical(None, _("Unable to create csv"), export_error_label + "\n" + str(reason))
self.show_message(_("Private keys exported."))
- def do_export_privkeys(self, fileName, pklist):
- with open(fileName, "w+") as csvfile:
- transaction = csv.writer(csvfile)
- transaction.writerow(["address", "private_key"])
- for addr, pk in pklist.items():
- transaction.writerow(["%34s"%addr,pk])
+ def do_export_privkeys(self, fileName, pklist, is_csv):
+ with open(fileName, "w+") as f:
+ if is_csv:
+ transaction = csv.writer(f)
+ transaction.writerow(["address", "private_key"])
+ for addr, pk in pklist.items():
+ transaction.writerow(["%34s"%addr,pk])
+ else:
+ import json
+ f.write(json.dumps(pklist, indent = 4))
def do_import_labels(self):
QMessageBox.critical(None, _("Unable to export labels"), _("Electrum was unable to export your labels.")+"\n" + str(reason))
- def do_export_history(self):
- wallet = self.wallet
- select_export = _('Select file to export your wallet transactions to')
- fileName = QFileDialog.getSaveFileName(QWidget(), select_export, os.path.expanduser('~/electrum-history.csv'), "*.csv")
- if not fileName:
- return
+ def export_history_dialog(self):
- try:
- with open(fileName, "w+") as csvfile:
- transaction = csv.writer(csvfile)
- transaction.writerow(["transaction_hash","label", "confirmations", "value", "fee", "balance", "timestamp"])
- for item in wallet.get_tx_history():
- tx_hash, confirmations, is_mine, value, fee, balance, timestamp = item
- if confirmations:
- if timestamp is not None:
- try:
- time_string = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
- except [RuntimeError, TypeError, NameError] as reason:
- time_string = "unknown"
- pass
- else:
- time_string = "unknown"
- else:
- time_string = "pending"
+ d = QDialog(self)
+ d.setWindowTitle(_('Export History'))
+ d.setMinimumSize(400, 200)
+ vbox = QVBoxLayout(d)
- if value is not None:
- value_string = format_satoshis(value, True)
- else:
- value_string = '--'
+ defaultname = os.path.expanduser('~/electrum-history.csv')
+ select_msg = _('Select file to export your wallet transactions to')
- if fee is not None:
- fee_string = format_satoshis(fee, True)
- else:
- fee_string = '0'
+ hbox, filename_e, csv_button = filename_field(self, self.config, defaultname, select_msg)
+ vbox.addLayout(hbox)
- if tx_hash:
- label, is_default_label = wallet.get_label(tx_hash)
- label = label.encode('utf-8')
- else:
- label = ""
+ vbox.addStretch(1)
- balance_string = format_satoshis(balance, False)
- transaction.writerow([tx_hash, label, confirmations, value_string, fee_string, balance_string, time_string])
- QMessageBox.information(None,_("CSV Export created"), _("Your CSV export has been successfully created."))
+ h, b = ok_cancel_buttons2(d, _('Export'))
+ vbox.addLayout(h)
+ if not d.exec_():
+ return
+ filename = filename_e.text()
+ if not filename:
+ return
+
+ try:
+ self.do_export_history(self.wallet, filename, csv_button.isChecked())
except (IOError, os.error), reason:
export_error_label = _("Electrum was unable to produce a transaction export.")
- QMessageBox.critical(None,_("Unable to create csv"), export_error_label + "\n" + str(reason))
+ QMessageBox.critical(self, _("Unable to export history"), export_error_label + "\n" + str(reason))
+ return
+
+ QMessageBox.information(self,_("History exported"), _("Your wallet history has been successfully exported."))
+
+
+ def do_export_history(self, wallet, fileName, is_csv):
+ history = wallet.get_tx_history()
+ lines = []
+ for item in history:
+ tx_hash, confirmations, is_mine, value, fee, balance, timestamp = item
+ if confirmations:
+ if timestamp is not None:
+ try:
+ time_string = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
+ except [RuntimeError, TypeError, NameError] as reason:
+ time_string = "unknown"
+ pass
+ else:
+ time_string = "unknown"
+ else:
+ time_string = "pending"
+
+ if value is not None:
+ value_string = format_satoshis(value, True)
+ else:
+ value_string = '--'
+
+ if fee is not None:
+ fee_string = format_satoshis(fee, True)
+ else:
+ fee_string = '0'
+
+ if tx_hash:
+ label, is_default_label = wallet.get_label(tx_hash)
+ label = label.encode('utf-8')
+ else:
+ label = ""
+
+ balance_string = format_satoshis(balance, False)
+ if is_csv:
+ lines.append([tx_hash, label, confirmations, value_string, fee_string, balance_string, time_string])
+ else:
+ lines.append({'txid':tx_hash, 'date':"%16s"%time_string, 'label':label, 'value':value_string})
+
+ with open(fileName, "w+") as f:
+ if is_csv:
+ transaction = csv.writer(f)
+ transaction.writerow(["transaction_hash","label", "confirmations", "value", "fee", "balance", "timestamp"])
+ for line in lines:
+ transaction.writerow(line)
+ else:
+ import json
+ f.write(json.dumps(lines, indent = 4))
def sweep_key_dialog(self):
d = QDialog(self)
d.setWindowTitle(_('Sweep private keys'))
+ d.setMinimumSize(600, 300)
vbox = QVBoxLayout(d)
vbox.addWidget(QLabel(_("Enter private keys")))
keys_e = QTextEdit()
keys_e.setTabChangesFocus(True)
vbox.addWidget(keys_e)
+
+ h, address_e = address_field(self.wallet.addresses())
+ vbox.addLayout(h)
+
vbox.addStretch(1)
hbox, button = ok_cancel_buttons2(d, _('Sweep'))
vbox.addLayout(hbox)
button.setEnabled(False)
- keys_e.textChanged.connect(lambda: button.setEnabled(Wallet.is_private_key(str(keys_e.toPlainText()).strip())))
+ def get_address():
+ addr = str(address_e.text())
+ if bitcoin.is_address(addr):
+ return addr
+
+ def get_pk():
+ pk = str(keys_e.toPlainText()).strip()
+ if Wallet.is_private_key(pk):
+ return pk.split()
+
+ f = lambda: button.setEnabled(get_address() is not None and get_pk() is not None)
+ keys_e.textChanged.connect(f)
+ address_e.textChanged.connect(f)
if not d.exec_():
return
- text = str(keys_e.toPlainText()).strip()
- privkeys = text.split()
- to_address = self.wallet.addresses()[0]
fee = self.wallet.fee
- tx = Transaction.sweep(privkeys, self.network, to_address, fee)
+ tx = Transaction.sweep(get_pk(), self.network, get_address(), fee)
self.show_transaction(tx)