create a class for transaction dialog
authorThomasV <thomasv@gitorious>
Sat, 14 Sep 2013 19:07:54 +0000 (21:07 +0200)
committerThomasV <thomasv@gitorious>
Sat, 14 Sep 2013 19:07:54 +0000 (21:07 +0200)
gui/gui_classic/main_window.py
gui/gui_classic/transaction_dialog.py [new file with mode: 0644]
lib/__init__.py
lib/commands.py
lib/interface.py
lib/network.py
lib/transaction.py
lib/util.py
setup.py

index 662b751..f6ac864 100644 (file)
@@ -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
new file mode 100644 (file)
index 0000000..ae18242
--- /dev/null
@@ -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'))
+
+
+
+
index 5182f42..fe4c7b3 100644 (file)
@@ -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
index 5e105d9..53a829d 100644 (file)
@@ -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)
index cf87442..97d1ac2 100644 (file)
@@ -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 = {}
index 9663da4..fba2937 100644 (file)
@@ -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"""
index 3136bfa..892ec5a 100644 (file)
@@ -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)
index 9157327..9503b88 100644 (file)
@@ -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()
 
index 79215c0..f4c13f4 100644 (file)
--- 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',