Example #1
0
    def create_outputs_tab(self):
        form = QFormLayout()
        self.outputs_tree = OutputsTree()
        self.outputs_editor = OutputsEditor(self.handler.gui, self.outputs_tree)
        self.outputs_editor.setEnabled(False)

        def update_enabled_widgets():
            num_outputs = len(self.outputs_tree.get_outputs())
            self.outputs_editor.setEnabled(num_outputs > 0)

        def add_output():
            new_output = CMutableTxOut(0)
            self.outputs_tree.add_output(new_output)

            update_enabled_widgets()
            if len(self.outputs_tree.get_outputs()) > 0:
                self.outputs_tree.view.selectRow(self.outputs_tree.model.rowCount() - 1)

        update_enabled_widgets()

        add_output_button = QPushButton('New output')
        add_output_button.setToolTip('Add a new output')
        add_output_button.clicked.connect(add_output)

        form.addRow(self.outputs_tree)
        form.addRow(Separator())

        form.addRow(self.outputs_editor)

        form.addRow(Separator())
        form.addRow(floated_buttons([add_output_button]))

        w = QWidget()
        w.setLayout(form)
        return w
Example #2
0
    def create_outputs_tab(self):
        form = QFormLayout()
        self.outputs_tree = OutputsTree()
        self.outputs_editor = OutputsEditor(self.handler.gui, self.outputs_tree)
        self.outputs_editor.setEnabled(False)

        def update_enabled_widgets():
            num_outputs = len(self.outputs_tree.get_outputs())
            self.outputs_editor.setEnabled(num_outputs > 0)

        def add_output():
            new_output = CMutableTxOut(0)
            self.outputs_tree.add_output(new_output)

            update_enabled_widgets()
            if len(self.outputs_tree.get_outputs()) > 0:
                self.outputs_tree.view.selectRow(self.outputs_tree.model.rowCount() - 1)

        update_enabled_widgets()

        add_output_button = QPushButton("New output")
        add_output_button.setToolTip("Add a new output")
        add_output_button.clicked.connect(add_output)

        form.addRow(self.outputs_tree)
        form.addRow(Separator())

        form.addRow(self.outputs_editor)

        form.addRow(Separator())
        form.addRow(floated_buttons([add_output_button]))

        w = QWidget()
        w.setLayout(form)
        return w
Example #3
0
    def create_outputs_tab(self):
        form = QFormLayout()
        self.outputs_tree = OutputsTree()
        self.outputs_tree.view.setWhatsThis('The outputs of your transaction are displayed here.')
        self.outputs_editor = OutputsEditor(self.handler.gui, self.outputs_tree)
        self.outputs_editor.setEnabled(False)

        def update_enabled_widgets():
            num_outputs = len(self.outputs_tree.get_outputs())
            self.outputs_editor.setEnabled(num_outputs > 0)

        def add_output():
            new_output = CMutableTxOut(0)
            self.outputs_tree.add_output(new_output)

            update_enabled_widgets()
            if len(self.outputs_tree.get_outputs()) > 0:
                self.outputs_tree.view.selectRow(self.outputs_tree.model.rowCount() - 1)

        update_enabled_widgets()

        add_output_button = QPushButton('New output')
        add_output_button.setToolTip('Add a new output')
        add_output_button.setWhatsThis('Clicking this button will add a new output to your transaction.')
        add_output_button.clicked.connect(add_output)

        form.addRow(self.outputs_tree)
        form.addRow(Separator())

        form.addRow(self.outputs_editor)

        form.addRow(Separator())
        form.addRow(floated_buttons([add_output_button]))

        w = QWidget()
        w.setLayout(form)
        return w
Example #4
0
class TxBuilder(BaseDock):

    tool_name = 'Transaction Builder'
    description = 'Transaction Builder helps you create transactions.'
    is_large = True
    category = Category.Tx

    def __init__(self, handler):
        super(TxBuilder, self).__init__(handler)
        self.raw_tx.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.raw_tx.customContextMenuRequested.connect(self.context_menu)

    @augmenter
    def item_actions(self, *args):
        return [
            ItemAction(self.tool_name, 'Transaction', 'Edit',
                       self.deserialize_item)
        ]

    def init_data(self):
        self.tx = None

    def create_layout(self):
        vbox = QVBoxLayout()
        self.tabs = tabs = QTabWidget()

        tabs.addTab(self.create_version_locktime_tab(), '&Version/Locktime')
        tabs.addTab(self.create_inputs_tab(), '&Inputs')
        tabs.addTab(self.create_outputs_tab(), '&Outputs')
        tabs.addTab(self.create_review_tab(), '&Review')
        tabs.addTab(self.create_sign_tab(), 'Sig&n')
        self.setFocusProxy(self.tabs)

        self.tx_field_widgets = []
        tabs.insertTab(3, self.create_other_tab(), 'Ot&her')
        self.adjust_tx_fields()

        # Build the tx if the Review tab is selected.
        def maybe_build(i):
            if str(tabs.tabText(i)) == '&Review' or str(
                    tabs.tabText(i)) == 'Sig&n':
                self.build_transaction()

        tabs.currentChanged.connect(maybe_build)

        vbox.addWidget(tabs)
        return vbox

    def context_menu(self, position):
        menu = self.raw_tx.createStandardContextMenu(position)

        txt = str(self.raw_tx.toPlainText())
        if txt:
            self.handler.add_plugin_actions(self, menu, txt)

        menu.exec_(self.raw_tx.viewport().mapToGlobal(position))

    def create_version_locktime_tab(self):
        form = QFormLayout()
        self.version_edit = AmountEdit()
        self.version_edit.setText('1')
        self.version_edit.setWhatsThis(
            'Use this field to specify the version of your transaction. In Bitcoin, transactions are currently version 1.'
        )

        self.locktime_edit = AmountEdit()
        self.locktime_edit.setText('0')
        self.locktime_edit.setWhatsThis(
            'Use this field to specify the locktime of your transaction. For most common transactions, locktime is zero.'
        )

        version_desc = QLabel(
            'A transaction\'s version determines how it is interpreted.\n\nBitcoin transactions are currently version 1.'
        )
        locktime_desc = QLabel(
            'A transaction\'s locktime defines the earliest time or block that it may be added to the blockchain.\n\nLocktime only applies if it\'s non-zero and at least one input has a Sequence that\'s not the maximum possible value.'
        )
        for i in [version_desc, locktime_desc]:
            i.setWordWrap(True)
        for i in [self.version_edit, self.locktime_edit]:
            i.setFont(monospace_font)

        form.addRow(version_desc)
        form.addRow('Version:', self.version_edit)
        form.addRow(Separator())
        form.addRow(locktime_desc)
        form.addRow('Locktime:', self.locktime_edit)

        w = QWidget()
        w.setLayout(form)
        return w

    def create_inputs_tab(self):
        form = QFormLayout()
        self.inputs_tree = InputsTree()
        self.inputs_tree.view.setWhatsThis(
            'The inputs of your transaction are displayed here.')
        self.inputs_editor = InputsEditor(self.handler.gui, self.inputs_tree)
        self.inputs_editor.setEnabled(False)

        def update_enabled_widgets():
            num_inputs = len(self.inputs_tree.get_inputs())
            self.inputs_editor.setEnabled(num_inputs > 0)

        def add_input():
            outpoint = CMutableOutPoint(n=0)
            new_input = CMutableTxIn(prevout=outpoint)
            self.inputs_tree.add_input(new_input)

            update_enabled_widgets()
            if len(self.inputs_tree.get_inputs()) > 0:
                self.inputs_tree.view.selectRow(
                    self.inputs_tree.model.rowCount() - 1)

        update_enabled_widgets()

        add_input_button = QPushButton('New input')
        add_input_button.setToolTip('Add a new input')
        add_input_button.setWhatsThis(
            'Clicking this button will add a new input to your transaction.')
        add_input_button.clicked.connect(add_input)

        form.addRow(self.inputs_tree)
        form.addRow(Separator())

        form.addRow(self.inputs_editor)

        form.addRow(Separator())
        form.addRow(floated_buttons([add_input_button]))

        w = QWidget()
        w.setLayout(form)
        return w

    def create_outputs_tab(self):
        form = QFormLayout()
        self.outputs_tree = OutputsTree()
        self.outputs_tree.view.setWhatsThis(
            'The outputs of your transaction are displayed here.')
        self.outputs_editor = OutputsEditor(self.handler.gui,
                                            self.outputs_tree)
        self.outputs_editor.setEnabled(False)

        def update_enabled_widgets():
            num_outputs = len(self.outputs_tree.get_outputs())
            self.outputs_editor.setEnabled(num_outputs > 0)

        def add_output():
            new_output = CMutableTxOut(0)
            self.outputs_tree.add_output(new_output)

            update_enabled_widgets()
            if len(self.outputs_tree.get_outputs()) > 0:
                self.outputs_tree.view.selectRow(
                    self.outputs_tree.model.rowCount() - 1)

        update_enabled_widgets()

        add_output_button = QPushButton('New output')
        add_output_button.setToolTip('Add a new output')
        add_output_button.setWhatsThis(
            'Clicking this button will add a new output to your transaction.')
        add_output_button.clicked.connect(add_output)

        form.addRow(self.outputs_tree)
        form.addRow(Separator())

        form.addRow(self.outputs_editor)

        form.addRow(Separator())
        form.addRow(floated_buttons([add_output_button]))

        w = QWidget()
        w.setLayout(form)
        return w

    def create_review_tab(self):
        form = QFormLayout()

        self.raw_tx = QTextEdit()
        self.raw_tx.setWhatsThis(
            'The transaction you build is displayed here.')
        self.raw_tx.setReadOnly(True)

        self.tx_widget = TxWidget()

        form.addRow('Raw Tx:', self.raw_tx)
        form.addRow(self.tx_widget)

        w = QWidget()
        w.setLayout(form)
        return w

    def create_other_tab(self):
        self.tx_fields_layout = QFormLayout()

        w = QWidget()
        w.setLayout(self.tx_fields_layout)
        return w

    def create_sign_tab(self):
        self.sighash_widget = SigHashWidget(self)
        return self.sighash_widget

    def deserialize_item(self, item):
        self.deserialize_raw(item.raw())

    def deserialize_raw(self, rawtx):
        """Update editor widgets with rawtx's data."""
        self.needsFocus.emit()
        try:
            tx = Transaction.deserialize(x(rawtx))
        except Exception:
            return
        else:
            self.version_edit.set_amount(tx.nVersion)
            self.inputs_tree.model.set_tx(tx)
            self.outputs_tree.model.set_tx(tx)
            self.locktime_edit.set_amount(tx.nLockTime)
            for name, w in self.tx_field_widgets:
                if name in ['nVersion', 'vin', 'vout', 'nLockTime']:
                    continue
                try:
                    value = getattr(tx, name)
                except AttributeError:
                    continue
                if isinstance(w, AmountEdit):
                    w.set_amount(value)
                else:
                    w.setText(str(value))
            self.build_transaction()

    def build_transaction(self):
        self.tx_widget.clear()
        self.sighash_widget.clear()
        self.tx = tx = Transaction()
        tx.nVersion = self.version_edit.get_amount()
        tx.vin = self.inputs_tree.get_inputs()
        tx.vout = self.outputs_tree.get_outputs()
        tx.nLockTime = self.locktime_edit.get_amount()

        for name, w in self.tx_field_widgets:
            if not name in [field[0] for field in tx.fields]:
                continue
            value = str(w.text())
            default = getattr(tx, name)
            if isinstance(default, int):
                value = w.get_amount()
            setattr(tx, name, value)

        self.raw_tx.setText(bitcoin.core.b2x(tx.serialize()))

        self.tx_widget.set_tx(tx)
        self.sighash_widget.set_tx(tx)

    def on_option_changed(self, key):
        if key == 'chainparams':
            self.needsUpdate.emit()

    def adjust_tx_fields(self):
        """Show or hide tx field widgets."""
        tx_fields = chainparams.get_tx_fields()
        for field in tx_fields:
            name = field[0]
            if name in ['nVersion', 'vin', 'vout', 'nLockTime']:
                continue

            default_value = field[3]
            if name not in [j[0] for j in self.tx_field_widgets]:
                widget = QLineEdit()
                if isinstance(default_value, int):
                    # Special case for timestamp fields.
                    if name == 'Timestamp':
                        widget = TimestampWidget()
                        widget.timestamp_raw.setReadOnly(False)
                    else:
                        widget = AmountEdit()
                widget.setText(str(default_value))
                label = QLabel(''.join([name, ':']))
                self.tx_field_widgets.append((name, widget))
                self.tx_fields_layout.addRow(label, widget)

        tx_field_names = [i[0] for i in tx_fields]
        for name, w in self.tx_field_widgets:
            l = self.tx_fields_layout.labelForField(w)
            if name in tx_field_names:
                w.show()
                l.show()
            else:
                w.hide()
                l.hide()

        if tx_field_names == ['nVersion', 'vin', 'vout', 'nLockTime']:
            self.tabs.setTabEnabled(3, False)
        else:
            self.tabs.setTabEnabled(3, True)

    def refresh_data(self):
        self.adjust_tx_fields()
        self.build_transaction()
Example #5
0
class TxBuilder(BaseDock):

    tool_name = 'Transaction Builder'
    description = 'Transaction Builder helps you create transactions.'
    is_large = True
    category = Category.Tx

    def __init__(self, handler):
        super(TxBuilder, self).__init__(handler)
        self.raw_tx.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.raw_tx.customContextMenuRequested.connect(self.context_menu)

    @augmenter
    def item_actions(self, *args):
        return [ItemAction(self.tool_name, 'Transaction', 'Edit', self.deserialize_item)]

    def init_data(self):
        self.tx = None

    def create_layout(self):
        vbox = QVBoxLayout()
        self.tabs = tabs = QTabWidget()

        tabs.addTab(self.create_version_locktime_tab(), '&Version/Locktime')
        tabs.addTab(self.create_inputs_tab(), '&Inputs')
        tabs.addTab(self.create_outputs_tab(), '&Outputs')
        tabs.addTab(self.create_review_tab(), '&Review')
        self.setFocusProxy(self.tabs)

        self.tx_field_widgets = []
        tabs.insertTab(3, self.create_other_tab(), 'Ot&her')
        self.adjust_tx_fields()

        # Build the tx if the Review tab is selected.
        def maybe_build(i):
            if str(tabs.tabText(i)) == '&Review':
                self.build_transaction()
        tabs.currentChanged.connect(maybe_build)

        vbox.addWidget(tabs)
        return vbox

    def context_menu(self, position):
        menu = self.raw_tx.createStandardContextMenu(position)

        txt = str(self.raw_tx.toPlainText())
        if txt:
            self.handler.add_plugin_actions(self, menu, txt)

        menu.exec_(self.raw_tx.viewport().mapToGlobal(position))

    def create_version_locktime_tab(self):
        form = QFormLayout()
        self.version_edit = AmountEdit()
        self.version_edit.setText('1')
        self.version_edit.setWhatsThis('Use this field to specify the version of your transaction. In Bitcoin, transactions are currently version 1.')

        self.locktime_edit = AmountEdit()
        self.locktime_edit.setText('0')
        self.locktime_edit.setWhatsThis('Use this field to specify the locktime of your transaction. For most common transactions, locktime is zero.')

        version_desc = QLabel('A transaction\'s version determines how it is interpreted.\n\nBitcoin transactions are currently version 1.')
        locktime_desc = QLabel('A transaction\'s locktime defines the earliest time or block that it may be added to the blockchain.\n\nLocktime only applies if it\'s non-zero and at least one input has a Sequence that\'s not the maximum possible value.')
        for i in [version_desc, locktime_desc]:
            i.setWordWrap(True)
        for i in [self.version_edit, self.locktime_edit]:
            i.setFont(monospace_font)

        form.addRow(version_desc)
        form.addRow('Version:', self.version_edit)
        form.addRow(Separator())
        form.addRow(locktime_desc)
        form.addRow('Locktime:', self.locktime_edit)

        w = QWidget()
        w.setLayout(form)
        return w

    def create_inputs_tab(self):
        form = QFormLayout()
        self.inputs_tree = InputsTree()
        self.inputs_tree.view.setWhatsThis('The inputs of your transaction are displayed here.')
        self.inputs_editor = InputsEditor(self.handler.gui, self.inputs_tree)
        self.inputs_editor.setEnabled(False)

        def update_enabled_widgets():
            num_inputs = len(self.inputs_tree.get_inputs())
            self.inputs_editor.setEnabled(num_inputs > 0)

        def add_input():
            outpoint = CMutableOutPoint(n=0)
            new_input = CMutableTxIn(prevout=outpoint)
            self.inputs_tree.add_input(new_input)

            update_enabled_widgets()
            if len(self.inputs_tree.get_inputs()) > 0:
                self.inputs_tree.view.selectRow(self.inputs_tree.model.rowCount() - 1)

        update_enabled_widgets()

        add_input_button = QPushButton('New input')
        add_input_button.setToolTip('Add a new input')
        add_input_button.setWhatsThis('Clicking this button will add a new input to your transaction.')
        add_input_button.clicked.connect(add_input)

        form.addRow(self.inputs_tree)
        form.addRow(Separator())

        form.addRow(self.inputs_editor)

        form.addRow(Separator())
        form.addRow(floated_buttons([add_input_button]))

        w = QWidget()
        w.setLayout(form)
        return w

    def create_outputs_tab(self):
        form = QFormLayout()
        self.outputs_tree = OutputsTree()
        self.outputs_tree.view.setWhatsThis('The outputs of your transaction are displayed here.')
        self.outputs_editor = OutputsEditor(self.handler.gui, self.outputs_tree)
        self.outputs_editor.setEnabled(False)

        def update_enabled_widgets():
            num_outputs = len(self.outputs_tree.get_outputs())
            self.outputs_editor.setEnabled(num_outputs > 0)

        def add_output():
            new_output = CMutableTxOut(0)
            self.outputs_tree.add_output(new_output)

            update_enabled_widgets()
            if len(self.outputs_tree.get_outputs()) > 0:
                self.outputs_tree.view.selectRow(self.outputs_tree.model.rowCount() - 1)

        update_enabled_widgets()

        add_output_button = QPushButton('New output')
        add_output_button.setToolTip('Add a new output')
        add_output_button.setWhatsThis('Clicking this button will add a new output to your transaction.')
        add_output_button.clicked.connect(add_output)

        form.addRow(self.outputs_tree)
        form.addRow(Separator())

        form.addRow(self.outputs_editor)

        form.addRow(Separator())
        form.addRow(floated_buttons([add_output_button]))

        w = QWidget()
        w.setLayout(form)
        return w

    def create_review_tab(self):
        form = QFormLayout()

        self.raw_tx = QTextEdit()
        self.raw_tx.setWhatsThis('The transaction you build is displayed here.')
        self.raw_tx.setReadOnly(True)

        self.tx_widget = TxWidget()

        form.addRow('Raw Tx:', self.raw_tx)
        form.addRow(self.tx_widget)

        w = QWidget()
        w.setLayout(form)
        return w

    def create_other_tab(self):
        self.tx_fields_layout = QFormLayout()

        w = QWidget()
        w.setLayout(self.tx_fields_layout)
        return w

    def deserialize_item(self, item):
        self.deserialize_raw(item.raw())

    def deserialize_raw(self, rawtx):
        """Update editor widgets with rawtx's data."""
        self.needsFocus.emit()
        try:
            tx = Transaction.deserialize(x(rawtx))
        except Exception:
            return
        else:
            self.version_edit.set_amount(tx.nVersion)
            self.inputs_tree.model.set_tx(tx)
            self.outputs_tree.model.set_tx(tx)
            self.locktime_edit.set_amount(tx.nLockTime)
            for name, w in self.tx_field_widgets:
                if name in ['nVersion', 'vin', 'vout', 'nLockTime']:
                    continue
                try:
                    value = getattr(tx, name)
                except AttributeError:
                    continue
                if isinstance(w, AmountEdit):
                    w.set_amount(value)
                else:
                    w.setText(str(value))
            self.build_transaction()

    def build_transaction(self):
        self.tx_widget.clear()
        self.tx = tx = Transaction()
        tx.nVersion = self.version_edit.get_amount()
        tx.vin = self.inputs_tree.get_inputs()
        tx.vout = self.outputs_tree.get_outputs()
        tx.nLockTime = self.locktime_edit.get_amount()

        for name, w in self.tx_field_widgets:
            if not name in [field[0] for field in tx.fields]:
                continue
            value = str(w.text())
            default = getattr(tx, name)
            if isinstance(default, int):
                value = w.get_amount()
            setattr(tx, name, value)

        self.raw_tx.setText(bitcoin.core.b2x(tx.serialize()))

        self.tx_widget.set_tx(tx)

    def on_option_changed(self, key):
        if key in ['chainparams']:
            self.needsUpdate.emit()

    def adjust_tx_fields(self):
        """Show or hide tx field widgets."""
        tx_fields = chainparams.get_tx_fields()
        for field in tx_fields:
            name = field[0]
            if name in ['nVersion', 'vin', 'vout', 'nLockTime']:
                continue

            default_value = field[3]
            if name not in [j[0] for j in self.tx_field_widgets]:
                widget = QLineEdit()
                if isinstance(default_value, int):
                    # Special case for timestamp fields.
                    if name == 'Timestamp':
                        widget = TimestampWidget()
                        widget.timestamp_raw.setReadOnly(False)
                    else:
                        widget = AmountEdit()
                widget.setText(str(default_value))
                label = QLabel(''.join([name, ':']))
                self.tx_field_widgets.append((name, widget))
                self.tx_fields_layout.addRow(label, widget)

        tx_field_names = [i[0] for i in tx_fields]
        for name, w in self.tx_field_widgets:
            l = self.tx_fields_layout.labelForField(w)
            if name in tx_field_names:
                w.show()
                l.show()
            else:
                w.hide()
                l.hide()

        if tx_field_names == ['nVersion', 'vin', 'vout', 'nLockTime']:
            self.tabs.setTabEnabled(3, False)
        else:
            self.tabs.setTabEnabled(3, True)

    def refresh_data(self):
        self.adjust_tx_fields()
        self.build_transaction()
Example #6
0
class TxBuilder(BaseDock):

    tool_name = "Transaction Builder"
    description = "Transaction Builder helps you create transactions."
    is_large = True
    category = Category.Tx

    def __init__(self, handler):
        super(TxBuilder, self).__init__(handler)
        self.raw_tx.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.raw_tx.customContextMenuRequested.connect(self.context_menu)

    def init_data(self):
        self.tx = None

    def init_actions(self):
        self.advertised_actions[RAW_TX] = [("Edit", self.deserialize_raw)]

    def create_layout(self):
        vbox = QVBoxLayout()
        self.tabs = tabs = QTabWidget()

        tabs.addTab(self.create_version_locktime_tab(), "&Version/Locktime")
        tabs.addTab(self.create_inputs_tab(), "&Inputs")
        tabs.addTab(self.create_outputs_tab(), "&Outputs")
        tabs.addTab(self.create_review_tab(), "&Review")
        self.setFocusProxy(self.tabs)

        self.tx_field_widgets = []
        tabs.insertTab(3, self.create_other_tab(), "Ot&her")
        self.adjust_tx_fields()

        # Build the tx if the Review tab is selected.
        def maybe_build(i):
            if str(tabs.tabText(i)) == "&Review":
                self.build_transaction()

        tabs.currentChanged.connect(maybe_build)

        vbox.addWidget(tabs)
        return vbox

    def context_menu(self, position):
        menu = self.raw_tx.createStandardContextMenu(position)

        txt = str(self.raw_tx.toPlainText())
        if txt:
            self.handler.add_plugin_actions(self, menu, RAW_TX, txt)

        menu.exec_(self.raw_tx.viewport().mapToGlobal(position))

    def create_version_locktime_tab(self):
        form = QFormLayout()
        self.version_edit = AmountEdit()
        self.version_edit.setText("1")

        self.locktime_edit = AmountEdit()
        self.locktime_edit.setText("0")

        version_desc = QLabel(
            "A transaction's version determines how it is interpreted.\n\nBitcoin transactions are currently version 1."
        )
        locktime_desc = QLabel(
            "A transaction's locktime defines the earliest time or block that it may be added to the blockchain.\n\nLocktime only applies if it's non-zero and at least one input has a Sequence that's not the maximum possible value."
        )
        for i in [version_desc, locktime_desc]:
            i.setWordWrap(True)
        for i in [self.version_edit, self.locktime_edit]:
            i.setFont(monospace_font)

        form.addRow(version_desc)
        form.addRow("Version:", self.version_edit)
        form.addRow(Separator())
        form.addRow(locktime_desc)
        form.addRow("Locktime:", self.locktime_edit)

        w = QWidget()
        w.setLayout(form)
        return w

    def create_inputs_tab(self):
        form = QFormLayout()
        self.inputs_tree = InputsTree()
        self.inputs_editor = InputsEditor(self.handler.gui, self.inputs_tree)
        self.inputs_editor.setEnabled(False)

        def update_enabled_widgets():
            num_inputs = len(self.inputs_tree.get_inputs())
            self.inputs_editor.setEnabled(num_inputs > 0)

        def add_input():
            outpoint = CMutableOutPoint(n=0)
            new_input = CMutableTxIn(prevout=outpoint)
            self.inputs_tree.add_input(new_input)

            update_enabled_widgets()
            if len(self.inputs_tree.get_inputs()) > 0:
                self.inputs_tree.view.selectRow(self.inputs_tree.model.rowCount() - 1)

        update_enabled_widgets()

        add_input_button = QPushButton("New input")
        add_input_button.setToolTip("Add a new input")
        add_input_button.clicked.connect(add_input)

        form.addRow(self.inputs_tree)
        form.addRow(Separator())

        form.addRow(self.inputs_editor)

        form.addRow(Separator())
        form.addRow(floated_buttons([add_input_button]))

        w = QWidget()
        w.setLayout(form)
        return w

    def create_outputs_tab(self):
        form = QFormLayout()
        self.outputs_tree = OutputsTree()
        self.outputs_editor = OutputsEditor(self.handler.gui, self.outputs_tree)
        self.outputs_editor.setEnabled(False)

        def update_enabled_widgets():
            num_outputs = len(self.outputs_tree.get_outputs())
            self.outputs_editor.setEnabled(num_outputs > 0)

        def add_output():
            new_output = CMutableTxOut(0)
            self.outputs_tree.add_output(new_output)

            update_enabled_widgets()
            if len(self.outputs_tree.get_outputs()) > 0:
                self.outputs_tree.view.selectRow(self.outputs_tree.model.rowCount() - 1)

        update_enabled_widgets()

        add_output_button = QPushButton("New output")
        add_output_button.setToolTip("Add a new output")
        add_output_button.clicked.connect(add_output)

        form.addRow(self.outputs_tree)
        form.addRow(Separator())

        form.addRow(self.outputs_editor)

        form.addRow(Separator())
        form.addRow(floated_buttons([add_output_button]))

        w = QWidget()
        w.setLayout(form)
        return w

    def create_review_tab(self):
        form = QFormLayout()

        self.raw_tx = QTextEdit()
        self.raw_tx.setReadOnly(True)

        self.tx_widget = TxWidget()

        form.addRow("Raw Tx:", self.raw_tx)
        form.addRow(self.tx_widget)

        w = QWidget()
        w.setLayout(form)
        return w

    def create_other_tab(self):
        self.tx_fields_layout = QFormLayout()

        w = QWidget()
        w.setLayout(self.tx_fields_layout)
        return w

    def deserialize_raw(self, rawtx):
        """Update editor widgets with rawtx's data."""
        self.needsFocus.emit()
        try:
            tx = Transaction.deserialize(x(rawtx))
        except Exception:
            return
        else:
            self.version_edit.set_amount(tx.nVersion)
            self.inputs_tree.model.set_tx(tx)
            self.outputs_tree.model.set_tx(tx)
            self.locktime_edit.set_amount(tx.nLockTime)
            for name, w in self.tx_field_widgets:
                if name in ["nVersion", "vin", "vout", "nLockTime"]:
                    continue
                value = getattr(tx, name)
                if isinstance(w, AmountEdit):
                    w.set_amount(value)
                else:
                    w.setText(str(value))
            self.build_transaction()

    def build_transaction(self):
        self.tx_widget.clear()
        self.tx = tx = Transaction()
        tx.nVersion = self.version_edit.get_amount()
        tx.vin = self.inputs_tree.get_inputs()
        tx.vout = self.outputs_tree.get_outputs()
        tx.nLockTime = self.locktime_edit.get_amount()

        for name, w in self.tx_field_widgets:
            if not name in [field[0] for field in tx.fields]:
                continue
            value = str(w.text())
            default = getattr(tx, name)
            if isinstance(default, int):
                value = w.get_amount()
            setattr(tx, name, value)

        self.raw_tx.setText(bitcoin.core.b2x(tx.serialize()))

        self.tx_widget.set_tx(tx)

    def on_option_changed(self, key):
        if key in ["chainparams"]:
            self.needsUpdate.emit()

    def adjust_tx_fields(self):
        """Show or hide tx field widgets."""
        tx_fields = chainparams.get_tx_fields()
        for field in tx_fields:
            name = field[0]
            if name in ["nVersion", "vin", "vout", "nLockTime"]:
                continue

            default_value = field[3]
            if name not in [j[0] for j in self.tx_field_widgets]:
                widget = QLineEdit()
                if isinstance(default_value, int):
                    # Special case for timestamp fields.
                    if name == "Timestamp":
                        widget = TimestampWidget()
                        widget.timestamp_raw.setReadOnly(False)
                    else:
                        widget = AmountEdit()
                widget.setText(str(default_value))
                label = QLabel("".join([name, ":"]))
                self.tx_field_widgets.append((name, widget))
                self.tx_fields_layout.addRow(label, widget)

        tx_field_names = [i[0] for i in tx_fields]
        for name, w in self.tx_field_widgets:
            l = self.tx_fields_layout.labelForField(w)
            if name in tx_field_names:
                w.show()
                l.show()
            else:
                w.hide()
                l.hide()

        if tx_field_names == ["nVersion", "vin", "vout", "nLockTime"]:
            self.tabs.setTabEnabled(3, False)
        else:
            self.tabs.setTabEnabled(3, True)

    def refresh_data(self):
        self.adjust_tx_fields()
        self.build_transaction()