def __init__(self, parent):
        super(QWidget, self).__init__(parent)
        self.parent = parent
        self.tree = ProposalsTreeWidget(parent)

        # Proposals that have been paid for but not submitted.
        self.unsubmitted_proposals = []

        description = ''.join([
            'You can create a budget proposal below. ',
            'Proposals require 5 AXE to create. ',
            'Your proposal can be submitted once the collateral transaction has enough confirmations.'
        ])
        description = QLabel(_(description))
        description.setWordWrap(True)

        self.name_edit = QLineEdit()
        self.name_edit.setPlaceholderText(_('Name of your proposal'))
        self.url_edit = QLineEdit()
        self.url_edit.setPlaceholderText(
            _('URL that your proposal can be found at'))
        self.payments_count_edit = QSpinBox()
        self.payments_count_edit.setRange(1, 1000000)
        self.start_block_edit = QSpinBox()
        self.start_block_edit.setRange(0, 1000000)

        self.address_edit = QLineEdit()
        self.address_edit.setPlaceholderText(
            _('Address that will receive payments'))

        self.amount_edit = BTCAmountEdit(self.parent.get_decimal_point)

        self.create_proposal_button = QPushButton(_('Create Proposal'))
        self.create_proposal_button.clicked.connect(self.create_proposal)
        self.submit_ready_proposals_button = QPushButton(
            _('Submit Confirmed Proposals'))
        self.submit_ready_proposals_button.clicked.connect(
            self.submit_waiting_proposals)
        self.submit_ready_proposals_button.setEnabled(False)
        self.ready_proposals = QLabel()
        self.ready_proposals.setVisible(False)

        form = QFormLayout()
        form.addRow(_('Proposal Name:'), self.name_edit)
        form.addRow(_('Proposal URL:'), self.url_edit)
        form.addRow(_('Number of Payments:'), self.payments_count_edit)
        form.addRow(_('Starting Block:'), self.start_block_edit)
        form.addRow(_('Payment Address:'), self.address_edit)
        form.addRow(_('Monthly AXE Payment:'), self.amount_edit)

        vbox = QVBoxLayout()
        vbox.addWidget(self.tree)
        vbox.addWidget(description)
        vbox.addLayout(form)

        vbox.addLayout(util.Buttons(self.create_proposal_button))
        vbox.addLayout(
            util.Buttons(self.ready_proposals,
                         self.submit_ready_proposals_button))
        self.setLayout(vbox)
Exemple #2
0
    def add_paytoedit(self):
        w = QWidget()
        grid = QGridLayout()
        grid.setSpacing(8)
        grid.setColumnMinimumWidth(1, 300)
        grid.setColumnStretch(1, 2)

        pay_edit = PayToEdit(self.parent,
                             BTCAmountEdit(self.parent.get_decimal_point))

        self.payto_widgets.append((w, pay_edit))
        current_widget_index = len(self.payto_widgets) - 1

        completer = QCompleter()
        completer.setCaseSensitivity(False)
        pay_edit.setCompleter(completer)
        completer.setModel(self.parent.completions)

        if current_widget_index == 0:
            grid.addWidget(QLabel(_('Pay to')), 0, 0, 1, 1)
            grid.addWidget(QLabel(_('Amount')), 0, 3, 1, 1)

            grid.addWidget(self.payto_help, 0, 1)
            grid.addWidget(self.amount_help, 0, 4)

        grid.addWidget(pay_edit, 1, 0, 1, 3)

        grid.addWidget(pay_edit.amount_edit, 1, 3, 1, 3)

        w.setLayout(grid)
        self.vbox.addWidget(w)

        pay_edit.amount_edit.shortcut.connect(
            functools.partial(self.on_shortcut, current_widget_index))
        pay_edit.textChanged.connect(
            functools.partial(self.on_textChanged, current_widget_index))
        pay_edit.amount_edit.textEdited.connect(
            functools.partial(self.on_textChanged, current_widget_index))

        return w
    def create_chain_options(self):
        rows = []
        gui = self.gui

        # Number of Zeroes
        nz_label = QLabel(_('Zeros after decimal point') + ':')
        nz_help = HelpButton(_('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"'))
        nz = QSpinBox()
        nz.setMinimum(0)
        nz.setMaximum(gui.decimal_point)
        nz.setValue(gui.num_zeros)
        if not self.config.is_modifiable('num_zeros'):
            for w in [nz, nz_label]: w.setEnabled(False)
        def on_nz():
            value = nz.value()
            if gui.num_zeros != value:
                gui.num_zeros = value
                self.config.set_key('num_zeros', value, True)
                gui.update_history_tab()
                gui.update_address_tab()
        nz.valueChanged.connect(on_nz)
        rows.append(SettingsRow(self,'combobox', (nz_label, nz, nz_help)))

        # Fee #
        fee_label = QLabel(_('Transaction fee per kb') + ':')
        fee_help = HelpButton(_('Fee per kilobyte of transaction.') + '\n' \
                              + _('Recommended value') + ': ' + gui.format_amount(gui.active_chain.RECOMMENDED_FEE) + ' ' + gui.base_unit())

        fee_e = BTCAmountEdit(gui.get_decimal_point)
        fee_e.setAmount(gui.wallet.fee_per_kb)
        if not self.config.is_modifiable('fee_per_kb'):
            for w in [fee_e, fee_label]: w.setEnabled(False)
        def on_fee():
            fee = fee_e.get_amount()
            gui.wallet.set_fee(fee)
        fee_e.editingFinished.connect(on_fee)
        rows.append(SettingsRow(self,'combobox', (fee_label, fee_e, fee_help)))

        # Base Unit #
        units = gui.base_units.keys()
        unit_label = QLabel(_('Base unit') + ':')
        unit_combo = QComboBox()
        unit_combo.addItems(units)
        unit_combo.setCurrentIndex(units.index(gui.base_unit()))
        msg = _('Base unit of your wallet.')\
              + '\n1BTC=1000mBTC.\n' \
              + _(' These settings affects the fields in the Send tab')+' '
        unit_help = HelpButton(msg)
        def on_unit(x):
            unit_result = units[unit_combo.currentIndex()]
            if gui.base_unit() == unit_result:
                return
            gui.decimal_point = gui.base_units[unit_result]
            self.config.set_key('decimal_point', gui.decimal_point, True)
            gui.update_history_tab()
            gui.update_receive_tab()
            gui.update_address_tab()
            gui.update_invoices_tab()
            fee_e.setAmount(gui.wallet.fee_per_kb)
            gui.update_status()
        unit_combo.currentIndexChanged.connect(on_unit)
        rows.append(SettingsRow(self,'combobox', (unit_label, unit_combo, unit_help)))

        # Block Explorers #
        block_explorers = gui.block_explorers.keys()
        block_ex_label = QLabel(_('Online Block Explorer') + ':')
        block_ex_combo = QComboBox()
        block_ex_combo.addItems(block_explorers)
        block_ex_combo.setCurrentIndex(block_explorers.index(self.config.get('block_explorer', block_explorers[0])))
        block_ex_help = HelpButton(_('Choose which online block explorer to use for functions that open a web browser'))
        def on_be(x):
            be_result = block_explorers[block_ex_combo.currentIndex()]
            self.config.set_key('block_explorer', be_result, True)
        block_ex_combo.currentIndexChanged.connect(on_be)
        rows.append(SettingsRow(self,'combobox', (block_ex_label, block_ex_combo, block_ex_help)))

        # Use Change Addresses #
        usechange_cb = QCheckBox(_('Use change addresses'))
        usechange_cb.setChecked(gui.wallet.use_change)
        usechange_help = HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions.'))
        if not self.config.is_modifiable('use_change'): usechange_cb.setEnabled(False)
        def on_usechange(x):
            usechange_result = x == Qt.Checked
            if gui.wallet.use_change != usechange_result:
                gui.wallet.use_change = usechange_result
                gui.wallet.storage.put('use_change', gui.wallet.use_change)
        usechange_cb.stateChanged.connect(on_usechange)
        rows.append(SettingsRow(self,'toggle', (usechange_cb, usechange_help)))

        # Show Tx Before Broadcast #
        showtx_cb = QCheckBox(_('Show transaction before broadcast'))
        showtx_cb.setChecked(self.config.get('show_before_broadcast', False))
        showtx_cb.stateChanged.connect(lambda x: self.config.set_key('show_before_broadcast', showtx_cb.isChecked()))
        showtx_help = HelpButton(_('Display the details of your transactions before broadcasting it.'))
        rows.append(SettingsRow(self,'toggle', (showtx_cb, showtx_help)))

        # Set Fees Manually #
        can_edit_fees_cb = QCheckBox(_('Set transaction fees manually'))
        can_edit_fees_cb.setChecked(self.config.get('can_edit_fees', False))
        def on_editfees(x):
            self.config.set_key('can_edit_fees', x == Qt.Checked)
            gui.update_fee_edit()
        can_edit_fees_cb.stateChanged.connect(on_editfees)
        can_edit_fees_help = HelpButton(_('This option lets you edit fees in the send tab.'))
        rows.append(SettingsRow(self,'toggle', (can_edit_fees_cb, can_edit_fees_help)))

        return rows
class ProposalsTab(QWidget):
    """Wallet tab for budget proposals."""
    def __init__(self, parent):
        super(QWidget, self).__init__(parent)
        self.parent = parent
        self.tree = ProposalsTreeWidget(parent)

        # Proposals that have been paid for but not submitted.
        self.unsubmitted_proposals = []

        description = ''.join([
            'You can create a budget proposal below. ',
            'Proposals require 5 AXE to create. ',
            'Your proposal can be submitted once the collateral transaction has enough confirmations.'
        ])
        description = QLabel(_(description))
        description.setWordWrap(True)

        self.name_edit = QLineEdit()
        self.name_edit.setPlaceholderText(_('Name of your proposal'))
        self.url_edit = QLineEdit()
        self.url_edit.setPlaceholderText(
            _('URL that your proposal can be found at'))
        self.payments_count_edit = QSpinBox()
        self.payments_count_edit.setRange(1, 1000000)
        self.start_block_edit = QSpinBox()
        self.start_block_edit.setRange(0, 1000000)

        self.address_edit = QLineEdit()
        self.address_edit.setPlaceholderText(
            _('Address that will receive payments'))

        self.amount_edit = BTCAmountEdit(self.parent.get_decimal_point)

        self.create_proposal_button = QPushButton(_('Create Proposal'))
        self.create_proposal_button.clicked.connect(self.create_proposal)
        self.submit_ready_proposals_button = QPushButton(
            _('Submit Confirmed Proposals'))
        self.submit_ready_proposals_button.clicked.connect(
            self.submit_waiting_proposals)
        self.submit_ready_proposals_button.setEnabled(False)
        self.ready_proposals = QLabel()
        self.ready_proposals.setVisible(False)

        form = QFormLayout()
        form.addRow(_('Proposal Name:'), self.name_edit)
        form.addRow(_('Proposal URL:'), self.url_edit)
        form.addRow(_('Number of Payments:'), self.payments_count_edit)
        form.addRow(_('Starting Block:'), self.start_block_edit)
        form.addRow(_('Payment Address:'), self.address_edit)
        form.addRow(_('Monthly AXE Payment:'), self.amount_edit)

        vbox = QVBoxLayout()
        vbox.addWidget(self.tree)
        vbox.addWidget(description)
        vbox.addLayout(form)

        vbox.addLayout(util.Buttons(self.create_proposal_button))
        vbox.addLayout(
            util.Buttons(self.ready_proposals,
                         self.submit_ready_proposals_button))
        self.setLayout(vbox)

    def get_model(self):
        """Get the model so that other widgets can display proposals."""
        return self.tree.model

    def update(self, proposals):
        self.tree.update(proposals, self.parent)
        self.start_block_edit.setMinimum(
            self.parent.network.get_local_height())

        # Check if we have unsubmitted proposals.
        self.update_unsubmitted_proposals()

    def update_unsubmitted_proposals(self):
        """Update the list of unsubmitted proposals."""
        self.unsubmitted_proposals = []
        for p in self.parent.masternode_manager.proposals:
            if p.fee_txid and not p.submitted and not p.rejected:
                confirmations, timestamp = self.parent.wallet.get_confirmations(
                    p.fee_txid)
                if confirmations < BUDGET_FEE_CONFIRMATIONS:
                    continue

                item = (p.proposal_name, p.fee_txid)
                if item not in self.unsubmitted_proposals:
                    self.unsubmitted_proposals.append(item)

        can_submit = len(self.unsubmitted_proposals) > 0
        self.submit_ready_proposals_button.setEnabled(can_submit)

        num = len(self.unsubmitted_proposals)
        noun = 'proposal%s' % ('' if num == 1 else 's')
        article = 'is' if num == 1 else 'are'
        self.ready_proposals.setText(
            str(num) + _(' %s %s ready to be submitted.' % (noun, article)))
        self.ready_proposals.setVisible(can_submit)

    def create_proposal_from_widgets(self):
        """Get BudgetProposal keyword arguments from our widgets and instantiate one."""
        kwargs = {}
        kwargs['proposal_name'] = str(self.name_edit.text())
        kwargs['proposal_url'] = str(self.url_edit.text())
        kwargs['start_block'] = self.start_block_edit.value()
        kwargs['address'] = str(self.address_edit.text())
        kwargs['payment_amount'] = self.amount_edit.get_amount()

        proposal = BudgetProposal(**kwargs)
        # Assign end_block using the number of payments.
        proposal.set_payments_count(self.payments_count_edit.value())
        # Raise if proposal is invalid.
        proposal.check_valid()
        return proposal

    def create_proposal(self):
        manager = self.parent.masternode_manager

        try:
            proposal = self.create_proposal_from_widgets()
        except Exception as e:
            return QMessageBox.critical(self, _('Error'), _(str(e)))

        pw = None
        if manager.wallet.use_encryption:
            pw = self.parent.password_dialog(msg=_(
                'Please enter your password to create a budget proposal.'))
            if pw is None:
                return

        manager.add_proposal(proposal)

        def sign_done(proposal, tx):
            print_error('proposal tx sign done: %s' % proposal.proposal_name)
            if tx:
                label = _('Budget Proposal Tx: ') + proposal.proposal_name
                self.parent.broadcast_transaction(tx, label)
                self.parent.masternode_manager.save()

        self.create_proposal_tx(proposal, pw, sign_done)

    def create_proposal_tx(self, proposal, pw, callback):
        """Create and sign the proposal fee transaction."""
        result = [False]

        def tx_thread():
            return self.parent.masternode_manager.create_proposal_tx(
                proposal.proposal_name, pw, save=False)

        def on_tx_made(tx):
            result[0] = tx

        def on_done():
            callback(proposal, result[0])

        self.waiting_dialog = util.WaitingDialog(self,
                                                 _('Creating Transaction...'),
                                                 tx_thread, on_tx_made,
                                                 on_done)
        self.waiting_dialog.start()

    def submit_waiting_proposals(self):
        """Submit the proposals that are ready to the network."""
        # Submit the proposals that are ready.
        results = [('', '', False)] * len(self.unsubmitted_proposals)

        def submit_thread():
            for i, (proposal_name,
                    txid) in enumerate(self.unsubmitted_proposals):
                errmsg, success = self.parent.masternode_manager.submit_proposal(
                    proposal_name, save=False)
                results[i] = (proposal_name, errmsg, success)
                if success:
                    print_error('Sucessfully submitted proposal "%s"' %
                                proposal_name)
                else:
                    print_error('Failed to submit proposal "%s": %s' %
                                (proposal_name, errmsg))
            return results

        def on_done():
            msg = ''
            for proposal_name, errmsg, success in results:
                if success:
                    msg += '<b>' + proposal_name + '</b>' + _(
                        ': submitted successfully.')
                else:
                    msg += '<b>' + proposal_name + '</b>' + _(
                        ': failed! "%s"' % errmsg)

                msg += '\n'
            QMessageBox.information(self, _('Results'), msg)
            self.update_unsubmitted_proposals()
            self.parent.masternode_manager.save()

        self.waiting_dialog = util.WaitingDialog(self,
                                                 _('Submitting Proposals...'),
                                                 submit_thread,
                                                 on_complete=on_done)
        self.waiting_dialog.start()