1 # -*- coding: utf-8 -*-
2 # Spesmilo -- Python Bitcoin user interface
3 # Copyright © 2011 Luke Dashjr
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, version 3 only.
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
14 # You should have received a copy of the GNU General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 from decimal import Decimal
19 from PySide.QtCore import *
20 from PySide.QtGui import *
21 from settings import humanAmount, humanToAmount, SpesmiloSettings
25 class SendDialog(QDialog):
26 def __init__(self, core = None, parent = None, uri = None, autostart = True):
27 super(SendDialog, self).__init__(parent)
29 _windows[id(self)] = self
31 formlay = QFormLayout()
32 self.destaddy = QLineEdit()
33 self.amount = QLineEdit()
34 self.amount.setAlignment(Qt.AlignRight|Qt.AlignVCenter)
35 amount_max_width = self.fontMetrics().averageCharWidth() * 10
36 self.amount.setMaximumWidth(amount_max_width)
37 dv = QDoubleValidator(self.amount)
39 dv.setNotation(QDoubleValidator.StandardNotation)
41 tv = QRegExpValidator(QRegExp('-?(?:[\d\\xe9d9-\\xe9df]+\.?|[\d\\xe9d9-\\xe9df]*\.[\d\\xe9d9-\\xe9df]{1,4})'), self.amount)
43 self.amount_unit = QComboBox(self)
44 self.amount_unit.addItem(self.tr("BTC"), "BTC")
45 self.amount_unit.addItem(self.tr("TBC"), "TBC")
46 self.amount_unit.currentIndexChanged.connect(self.update_validator)
47 prefunit = 'TBC' if SpesmiloSettings.getNumberSystem() == 'Tonal' else 'BTC'
48 self.amount_unit.setCurrentIndex(self.amount_unit.findData(prefunit))
49 formlay.addRow(self.tr('Pay to:'), self.destaddy)
50 amountlay = QHBoxLayout()
51 amountlay.addWidget(self.amount)
52 amountlay.addWidget(self.amount_unit)
53 amountlay.addStretch()
54 formlay.addRow(self.tr('Amount:'), amountlay)
56 actionlay = QHBoxLayout()
57 sendbtn = QPushButton(self.tr('&Send'))
58 sendbtn.clicked.connect(self.do_payment)
59 sendbtn.setAutoDefault(True)
60 cancelbtn = QPushButton(self.tr('&Cancel'))
61 cancelbtn.clicked.connect(self.reject)
62 actionlay.addStretch()
63 actionlay.addWidget(sendbtn)
64 actionlay.addWidget(cancelbtn)
66 # layout includes form + instructions
67 instructions = QLabel(self.tr('<i>Enter a bitcoin address (e.g. 1A9Pv2PYuZYvfqku7sJxovw99Az72mZ4YH)</i>'))
68 mainlay = QVBoxLayout(self)
69 mainlay.addWidget(instructions)
70 mainlay.addLayout(formlay)
71 mainlay.addLayout(actionlay)
73 if parent is not None:
74 self.setWindowIcon(parent.bitcoin_icon)
75 self.setWindowTitle(self.tr('Send bitcoins'))
83 def start(self, options = None, args = None):
86 uri = SpesmiloSettings.getEffectiveURI()
87 self.core = core_interface.CoreInterface(uri)
89 self.load_uri(args[0])
92 self.destaddy.setFocus()
94 def load_uri(self, uri):
95 m = re.match(r'^bitcoin\:([1-9A-HJ-NP-Za-km-z]*)(?:\?(.*))?$', uri)
97 raise RuntimeError(self.tr('Invalid bitcoin URI'))
101 if not query is None:
102 query = re.split(r'[&?;]', query)
104 k, v = q.split('=', 1)
106 self.destaddy.setText(addr)
107 if 'amount' in param:
108 amount = param['amount']
109 m = re.match(r'^(([\d.]+)(X(\d+))?|x([\da-f]*)(\.([\da-f]*))?(X([\da-f]+))?)$', amount, re.IGNORECASE)
112 amount = float(int(m.group(5), 16))
114 amount += float(int(m.group(7), 16)) * pow(16, -(len(m.group(7))))
116 amount *= pow(16, int(m.group(9), 16))
121 amount = Decimal(m.group(2))
123 amount *= 10 ** int(m.group(4))
128 NU = SpesmiloSettings.ChooseUnits(amount, NU)
130 self.amount_unit.setCurrentIndex(self.amount_unit.findData('TBC'))
131 amount = SpesmiloSettings._toTBC(amount, addSign=False, wantTLA=False)
133 self.amount_unit.setCurrentIndex(self.amount_unit.findData('BTC'))
134 amount = SpesmiloSettings._toBTC(amount, addSign=False, wantTLA=False)
135 self.amount.setText(amount)
137 def update_validator(self):
138 u = self.amount_unit.itemData(self.amount_unit.currentIndex())
140 self.amount.setValidator(self.dv)
142 self.amount.setValidator(self.tv)
144 self.amount.setValidator(None)
146 def do_payment(self):
147 if not self.amount.text():
148 self.amount.setFocus(Qt.OtherFocusReason)
152 addy = self.destaddy.text()
153 if not self.core.validate_address(addy):
154 error = QMessageBox(QMessageBox.Critical,
155 self.tr('Invalid address'),
156 self.tr('Invalid address: %s')%addy)
161 amount = self.amount.text()
162 amount += ' ' + self.amount_unit.itemData(self.amount_unit.currentIndex())
163 amount = humanToAmount(amount)
165 balance = self.core.balance()
167 error = QMessageBox(QMessageBox.Critical,
168 self.tr('Insufficient balance'),
169 self.tr('Balance of %g is too small.') % (humanAmount(balance),))
175 self.core.send(addy, amount)
177 error = QMessageBox(QMessageBox.Critical,
178 self.tr('Error sending'),
179 self.tr('Your send failed: %s') % (e,))
186 del _windows[id(self)]
190 del _windows[id(self)]
193 if __name__ == '__main__':
196 import core_interface
197 from settings import SpesmiloSettings
198 os.system('bitcoind')
199 app = QApplication(sys.argv)
200 SpesmiloSettings.loadTranslator()
201 send = SendDialog(None, None, sys.argv[1] if len(sys.argv) > 1 else None, autostart=False)
203 sys.exit(app.exec_())