commit d51a8d0f252b3a57d0b2ab1e2149890d97322824
parent 27977e6eb0624075d74c773310edad2138db2ef4
Author: ThomasV <thomasv@gitorious>
Date: Sat, 14 Sep 2013 21:07:54 +0200
create a class for transaction dialog
Diffstat:
9 files changed, 257 insertions(+), 149 deletions(-)
diff --git a/gui/gui_classic/main_window.py b/gui/gui_classic/main_window.py
@@ -612,59 +612,16 @@ class ElectrumWindow(QMainWindow):
tx_hash = str(item.data(0, Qt.UserRole).toString())
if not tx_hash: return
menu = QMenu()
- #menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash))
- menu.addAction(_("Details"), lambda: self.show_tx_details(self.wallet.transactions.get(tx_hash)))
+ menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash))
+ menu.addAction(_("Details"), lambda: self.show_transaction(self.wallet.transactions.get(tx_hash)))
menu.addAction(_("Edit description"), lambda: self.tx_label_clicked(item,2))
menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
- def show_tx_details(self, tx):
- dialog = QDialog(self)
- dialog.setModal(1)
- dialog.setWindowTitle(_("Transaction Details"))
- vbox = QVBoxLayout()
- dialog.setLayout(vbox)
- dialog.setMinimumSize(600,300)
-
- tx_hash = tx.hash()
- if tx_hash in self.wallet.transactions.keys():
- is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx)
- conf, timestamp = self.wallet.verifier.get_confirmations(tx_hash)
- if timestamp:
- time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
- else:
- time_str = 'pending'
- else:
- is_mine = False
-
- vbox.addWidget(QLabel("Transaction ID:"))
- e = QLineEdit(tx_hash)
- e.setReadOnly(True)
- vbox.addWidget(e)
-
- vbox.addWidget(QLabel("Date: %s"%time_str))
- vbox.addWidget(QLabel("Status: %d confirmations"%conf))
- if is_mine:
- if fee is not None:
- vbox.addWidget(QLabel("Amount sent: %s"% self.format_amount(v-fee)))
- vbox.addWidget(QLabel("Transaction fee: %s"% self.format_amount(fee)))
- else:
- vbox.addWidget(QLabel("Amount sent: %s"% self.format_amount(v)))
- vbox.addWidget(QLabel("Transaction fee: unknown"))
- else:
- vbox.addWidget(QLabel("Amount received: %s"% self.format_amount(v)))
-
- vbox.addWidget( self.generate_transaction_information_widget(tx) )
-
- ok_button = QPushButton(_("Close"))
- ok_button.setDefault(True)
- ok_button.clicked.connect(dialog.accept)
-
- hbox = QHBoxLayout()
- hbox.addStretch(1)
- hbox.addWidget(ok_button)
- vbox.addLayout(hbox)
- dialog.exec_()
+ def show_transaction(self, tx):
+ import transaction_dialog
+ d = transaction_dialog.TxDialog(tx, self)
+ d.exec_()
def tx_label_clicked(self, item, column):
if column==2 and item.isSelected():
@@ -1678,45 +1635,6 @@ class ElectrumWindow(QMainWindow):
- def generate_transaction_information_widget(self, tx):
- tabs = QTabWidget(self)
-
- tab1 = QWidget()
- grid_ui = QGridLayout(tab1)
- grid_ui.setColumnStretch(0,1)
- tabs.addTab(tab1, _('Outputs') )
-
- tree_widget = MyTreeWidget(self)
- tree_widget.setColumnCount(2)
- tree_widget.setHeaderLabels( [_('Address'), _('Amount')] )
- tree_widget.setColumnWidth(0, 300)
- tree_widget.setColumnWidth(1, 50)
-
- for address, value in tx.outputs:
- item = QTreeWidgetItem( [address, "%s" % ( self.format_amount(value))] )
- tree_widget.addTopLevelItem(item)
-
- tree_widget.setMaximumHeight(100)
-
- grid_ui.addWidget(tree_widget)
-
- tab2 = QWidget()
- grid_ui = QGridLayout(tab2)
- grid_ui.setColumnStretch(0,1)
- tabs.addTab(tab2, _('Inputs') )
-
- tree_widget = MyTreeWidget(self)
- tree_widget.setColumnCount(2)
- tree_widget.setHeaderLabels( [ _('Address'), _('Previous output')] )
-
- for input_line in tx.inputs:
- item = QTreeWidgetItem( [ str(input_line["address"]), str(input_line["prevout_hash"])] )
- tree_widget.addTopLevelItem(item)
-
- tree_widget.setMaximumHeight(100)
-
- grid_ui.addWidget(tree_widget)
- return tabs
def tx_dict_from_text(self, txt):
@@ -1747,28 +1665,9 @@ class ElectrumWindow(QMainWindow):
@protected
def sign_raw_transaction(self, tx, input_info, dialog ="", password = ""):
- try:
- self.wallet.signrawtransaction(tx, input_info, [], password)
-
- fileName = self.getSaveFileName(_("Select where to save your signed transaction"), 'signed_%s.txn' % (tx.hash()[0:8]), "*.txn")
- if fileName:
- with open(fileName, "w+") as f:
- f.write(json.dumps(tx.as_dict(),indent=4) + '\n')
- self.show_message(_("Transaction saved successfully"))
- if dialog:
- dialog.done(0)
- except BaseException, e:
- self.show_message(str(e))
+ self.wallet.signrawtransaction(tx, input_info, [], password)
- def send_raw_transaction(self, raw_tx, dialog = ""):
- result, result_message = self.wallet.sendtx( raw_tx )
- if result:
- self.show_message("Transaction successfully sent: %s" % (result_message))
- if dialog:
- dialog.done(0)
- else:
- self.show_message("There was a problem sending your transaction:\n %s" % (result_message))
def do_process_from_text(self):
text = text_dialog(self, _('Input raw transaction'), _("Transaction:"), _("Load transaction"))
@@ -1801,8 +1700,9 @@ class ElectrumWindow(QMainWindow):
self.show_message(str(e))
return
- tx_dict = tx.as_dict()
- self.create_process_transaction_window(tx_dict)
+ self.show_transaction(tx)
+ #tx_dict = tx.as_dict()
+ #self.create_process_transaction_window(tx_dict)
def do_process_from_csv_file(self):
fileName = self.getOpenFileName(_("Select your transaction CSV"), "*.csv")
@@ -1824,41 +1724,10 @@ class ElectrumWindow(QMainWindow):
csvReader = csv.reader(f)
self.do_process_from_csvReader(csvReader)
+
def create_process_transaction_window(self, tx_dict):
tx = Transaction(tx_dict["hex"])
-
- dialog = QDialog(self)
- dialog.setMinimumWidth(500)
- dialog.setWindowTitle(_('Process raw transaction'))
- dialog.setModal(1)
-
- l = QGridLayout()
- dialog.setLayout(l)
-
- l.addWidget(QLabel(_("Transaction status:")), 3,0)
- l.addWidget(QLabel(_("Actions")), 4,0)
-
- if tx_dict["complete"] == False:
- l.addWidget(QLabel(_("Unsigned")), 3,1)
- if self.wallet.seed :
- b = QPushButton("Sign transaction")
- input_info = json.loads(tx_dict["input_info"])
- b.clicked.connect(lambda: self.sign_raw_transaction(tx, input_info, dialog))
- l.addWidget(b, 4, 1)
- else:
- l.addWidget(QLabel(_("Wallet is de-seeded, can't sign.")), 4,1)
- else:
- l.addWidget(QLabel(_("Signed")), 3,1)
- b = QPushButton("Broadcast transaction")
- b.clicked.connect(lambda: self.send_raw_transaction(tx, dialog))
- l.addWidget(b,4,1)
-
- l.addWidget( self.generate_transaction_information_widget(tx), 0,0,2,3)
- cancelButton = QPushButton(_("Cancel"))
- cancelButton.clicked.connect(lambda: dialog.done(0))
- l.addWidget(cancelButton, 4,2)
-
- dialog.exec_()
+ self.show_transaction(tx)
@protected
diff --git a/gui/gui_classic/transaction_dialog.py b/gui/gui_classic/transaction_dialog.py
@@ -0,0 +1,220 @@
+#!/usr/bin/env python
+#
+# Electrum - lightweight Bitcoin client
+# Copyright (C) 2012 thomasv@gitorious
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import sys, time, datetime, re, threading
+from electrum.i18n import _, set_language
+from electrum.util import print_error, print_msg
+import os.path, json, ast, traceback
+import shutil
+import StringIO
+
+
+try:
+ import PyQt4
+except:
+ sys.exit("Error: Could not import PyQt4 on Linux systems, you may try 'sudo apt-get install python-qt4'")
+
+from PyQt4.QtGui import *
+from PyQt4.QtCore import *
+import PyQt4.QtCore as QtCore
+
+from electrum import transaction
+
+
+class TxDialog(QDialog):
+
+ def __init__(self, tx, parent):
+ self.tx = tx
+ tx_dict = tx.as_dict()
+ self.parent = parent
+ self.wallet = parent.wallet
+
+ QDialog.__init__(self)
+ self.setMinimumWidth(600)
+ self.setWindowTitle(_('Transaction'))
+ self.setModal(1)
+
+ vbox = QVBoxLayout()
+ self.setLayout(vbox)
+
+ vbox.addWidget(QLabel("Transaction ID:"))
+ self.tx_hash_e = QLineEdit()
+ self.tx_hash_e.setReadOnly(True)
+ vbox.addWidget(self.tx_hash_e)
+ self.status_label = QLabel()
+ vbox.addWidget(self.status_label)
+
+ self.date_label = QLabel()
+ vbox.addWidget(self.date_label)
+ self.amount_label = QLabel()
+ vbox.addWidget(self.amount_label)
+ self.fee_label = QLabel()
+ vbox.addWidget(self.fee_label)
+
+ self.io = self.io_widget(tx)
+ vbox.addWidget( self.io )
+
+ buttons = QHBoxLayout()
+ vbox.addLayout( buttons )
+
+ buttons.addStretch(1)
+
+ self.sign_button = b = QPushButton(_("Sign"))
+ b.clicked.connect(self.sign)
+ buttons.addWidget(b)
+
+ self.broadcast_button = b = QPushButton(_("Broadcast"))
+ b.clicked.connect(self.broadcast)
+ b.hide()
+ buttons.addWidget(b)
+
+ self.save_button = b = QPushButton(_("Save"))
+ b.clicked.connect(self.save)
+ buttons.addWidget(b)
+
+ cancelButton = QPushButton(_("Close"))
+ cancelButton.clicked.connect(lambda: self.done(0))
+ buttons.addWidget(cancelButton)
+
+ self.update()
+
+
+
+
+ def sign(self):
+ tx_dict = self.tx.as_dict()
+ input_info = json.loads(tx_dict["input_info"])
+ self.parent.sign_raw_transaction(self.tx, input_info, self)
+ self.update()
+
+
+ def save(self):
+ fileName = self.parent.getSaveFileName(_("Select where to save your signed transaction"), 'signed_%s.txn' % (self.tx.hash()[0:8]), "*.txn")
+ if fileName:
+ with open(fileName, "w+") as f:
+ f.write(json.dumps(self.tx.as_dict(),indent=4) + '\n')
+ self.show_message(_("Transaction saved successfully"))
+
+
+ def update(self):
+ tx_hash = self.tx.hash()
+
+ is_relevant, is_mine, v, fee = self.wallet.get_tx_value(self.tx)
+
+ if self.tx.is_complete:
+ status = "Status: Signed"
+ self.sign_button.hide()
+
+ if tx_hash in self.wallet.transactions.keys():
+ conf, timestamp = self.wallet.verifier.get_confirmations(tx_hash)
+ if timestamp:
+ time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
+ else:
+ time_str = 'pending'
+ status = "Status: %d confirmations"%conf
+ self.broadcast_button.hide()
+ else:
+ time_str = None
+ conf = 0
+ self.broadcast_button.show()
+ else:
+ status = "Status: Unsigned"
+ time_str = None
+ self.sign_button.show()
+ self.broadcast_button.hide()
+
+ self.tx_hash_e.setText(tx_hash)
+ self.status_label.setText(status)
+
+ if time_str is not None:
+ self.date_label.setText("Date: %s"%time_str)
+ self.date_label.show()
+ else:
+ self.date_label.hide()
+
+ if is_relevant:
+ if is_mine:
+ if fee is not None:
+ self.amount_label.setText("Amount sent: %s"% self.parent.format_amount(v-fee))
+ self.fee_label.setText("Transaction fee: %s"% self.parent.format_amount(fee))
+ else:
+ self.amount_label.setText("Amount sent: %s"% self.parent.format_amount(v))
+ self.fee_label.setText("Transaction fee: unknown")
+ else:
+ self.amount_label.setText("Amount received: %s"% self.parent.format_amount(v))
+ else:
+ self.amount_label.setText("Transaction unrelated to your wallet")
+
+
+
+ def io_widget(self, tx):
+ tabs = QTabWidget(self)
+
+ tab1 = QWidget()
+ grid_ui = QGridLayout(tab1)
+ grid_ui.setColumnStretch(0,1)
+ tabs.addTab(tab1, _('Outputs') )
+
+ tree_widget = QTreeWidget(self)
+ tree_widget.setColumnCount(2)
+ tree_widget.setHeaderLabels( [_('Address'), _('Amount')] )
+ tree_widget.setColumnWidth(0, 300)
+ tree_widget.setColumnWidth(1, 50)
+
+ for address, value in tx.outputs:
+ item = QTreeWidgetItem( [address, "%s" % ( self.parent.format_amount(value))] )
+ tree_widget.addTopLevelItem(item)
+
+ tree_widget.setMaximumHeight(100)
+
+ grid_ui.addWidget(tree_widget)
+
+ tab2 = QWidget()
+ grid_ui = QGridLayout(tab2)
+ grid_ui.setColumnStretch(0,1)
+ tabs.addTab(tab2, _('Inputs') )
+
+ tree_widget = QTreeWidget(self)
+ tree_widget.setColumnCount(2)
+ tree_widget.setHeaderLabels( [ _('Address'), _('Previous output')] )
+
+ for input_line in tx.inputs:
+ item = QTreeWidgetItem( [ str(input_line["address"]), str(input_line.get("prevout_hash"))] )
+ tree_widget.addTopLevelItem(item)
+
+ tree_widget.setMaximumHeight(100)
+
+ grid_ui.addWidget(tree_widget)
+ return tabs
+
+
+ def broadcast(self):
+ result, result_message = self.wallet.sendtx( self.tx )
+ if result:
+ self.show_message("Transaction successfully sent: %s" % (result_message))
+ if dialog:
+ dialog.done(0)
+ else:
+ self.show_message("There was a problem sending your transaction:\n %s" % (result_message))
+
+ def show_message(self, msg):
+ QMessageBox.information(self, _('Message'), msg, _('OK'))
+
+
+
+
diff --git a/lib/__init__.py b/lib/__init__.py
@@ -8,6 +8,7 @@ from interface import Interface
from simple_config import SimpleConfig
import bitcoin
import account
+import transaction
from transaction import Transaction
from plugins import BasePlugin
from mnemonic import mn_encode as mnemonic_encode
diff --git a/lib/commands.py b/lib/commands.py
@@ -247,7 +247,7 @@ class Commands:
def mktx(self, to_address, amount, fee = None, change_addr = None, domain = None):
tx = self._mktx([(to_address, amount)], fee, change_addr, domain)
- return tx.as_dict()
+ return tx
def mksendmanytx(self, outputs, fee = None, change_addr = None, domain = None):
tx = self._mktx(outputs, fee, change_addr, domain)
diff --git a/lib/interface.py b/lib/interface.py
@@ -449,7 +449,7 @@ class Interface(threading.Thread):
def synchronous_get(self, requests, timeout=100000000):
# todo: use generators, unanswered_requests should be a list of arrays...
- q = Queue.Queue()
+ queue = Queue.Queue()
ids = self.send(requests, lambda i,r: queue.put(r))
id2 = ids[:]
res = {}
diff --git a/lib/network.py b/lib/network.py
@@ -174,6 +174,12 @@ class Network(threading.Thread):
def is_running(self):
with self.lock: return self.running
+
+ def retrieve_transaction(self, tx_hash, tx_height=0):
+ import transaction
+ r = self.interface.synchronous_get([ ('blockchain.transaction.get',[tx_hash, tx_height]) ])[0]
+ return transaction.Transaction(r)
+
def parse_servers(self, result):
""" parse servers list into dict format"""
diff --git a/lib/transaction.py b/lib/transaction.py
@@ -379,6 +379,13 @@ class Transaction:
self.input_info = None
self.is_complete = True
+
+ def __repr__(self):
+ return "Transaction('"+self.raw+"')"
+
+ def __str__(self):
+ return self.raw
+
@classmethod
def from_io(klass, inputs, outputs):
raw = klass.serialize(inputs, outputs, for_sig = -1) # for_sig=-1 means do not sign
@@ -390,12 +397,13 @@ class Transaction:
for i in self.inputs:
e = { 'txid':i['tx_hash'], 'vout':i['index'], 'scriptPubKey':i.get('raw_output_script') }
extras.append(e)
+ # fixme: simplify this
+ i['prevout_hash'] = i['tx_hash']
+ i['prevout_n'] = i['index']
+
self.input_info = extras
return self
- def __str__(self):
- return self.raw
-
@classmethod
def multisig_script(klass, public_keys, num=None):
n = len(public_keys)
diff --git a/lib/util.py b/lib/util.py
@@ -24,7 +24,10 @@ def print_msg(*args):
def print_json(obj):
import json
- s = json.dumps(obj,sort_keys = True, indent = 4)
+ try:
+ s = json.dumps(obj,sort_keys = True, indent = 4)
+ except TypeError:
+ s = repr(obj)
sys.stdout.write(s + "\n")
sys.stdout.flush()
diff --git a/setup.py b/setup.py
@@ -90,6 +90,7 @@ setup(name = "Electrum",
'electrum_gui.gui_classic.network_dialog',
'electrum_gui.gui_classic.password_dialog',
'electrum_gui.gui_classic.seed_dialog',
+ 'electrum_gui.gui_classic.transaction dialog',
'electrum_gui.gui_classic.version_getter',
'electrum_gui.gui_classic.amountedit',
'electrum_plugins.pointofsale',