first commit
[bitcoin:spesmilo.git] / cashier.py
1 from PySide.QtCore import *
2 from PySide.QtGui import *
3 from PySide.QtWebKit import *
4 import send
5
6 class FocusLineEdit(QLineEdit):
7     def __init__(self, text):
8         super(FocusLineEdit, self).__init__(text)
9         self.setReadOnly(True)
10         self.setMaxLength(40)
11
12     def mousePressEvent(self, event):
13         if event.button() == Qt.LeftButton:
14             self.setCursorPosition(100)
15             self.selectAll()
16             event.accept()
17         else:
18             super(FocusLineEdit, self).mousePressEvent(event)
19
20     def focusOutEvent(self, event):
21         event.accept()
22
23     def sizeHint(self):
24         sizeh = super(FocusLineEdit, self).sizeHint()
25         sizeh.setWidth(self.fontMetrics().averageCharWidth() * self.maxLength())
26         return sizeh
27
28 class TransactionItem(QTableWidgetItem):
29     def __init__(self, text, align=Qt.AlignLeft):
30         super(TransactionItem, self).__init__(text)
31         self.setFlags(Qt.ItemIsEnabled)
32         self.setTextAlignment(align|Qt.AlignVCenter)
33
34 class TransactionsTable(QTableWidget):
35     # These are the proportions for the various columns
36     hedprops = (130, 150, 400, 100, 100)
37
38     def __init__(self):
39         super(TransactionsTable, self).__init__()
40
41         self.setColumnCount(5)
42         hedlabels = (self.tr('Status'),
43                      self.tr('Date'),
44                      self.tr('Transactions'),
45                      self.tr('Credits'),
46                      self.tr('Balance'))
47         self.setHorizontalHeaderLabels(hedlabels)
48         for i, sz in enumerate(self.hedprops):
49             self.horizontalHeader().resizeSection(i, sz)
50
51         self.setSelectionBehavior(self.SelectRows)
52         self.setSelectionMode(self.NoSelection)
53         self.setFocusPolicy(Qt.NoFocus)
54         self.setAlternatingRowColors(True)
55         self.verticalHeader().hide()
56         self.setShowGrid(False)
57         self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
58         self.horizontalHeader().setStretchLastSection(True)
59
60     # Resize columns while maintaining proportions
61     def resizeEvent(self, event):
62         self_width = event.size().width()
63         total_prop_width = sum(self.hedprops)
64         newszs = [sz * self_width / total_prop_width for sz in self.hedprops]
65         for i, sz in enumerate(newszs):
66             self.horizontalHeader().resizeSection(i, sz)
67
68     def add_transaction_entry(self, transaction):
69         self.insertRow(0)
70         confirms = transaction['confirmations']
71         unixtime = transaction['time']
72         address = transaction['address']
73         credit =  transaction['amount']
74         balance = 'N/A'
75         category = transaction['category']
76
77         row_disabled = False
78         if confirms > 5:
79             status = self.tr('Confirmed (%i)')%confirms
80         elif confirms > 1:
81             status = self.tr('Processing... (%i)')%confirms
82         else:
83             status = self.tr('Validating... (%i)')%confirms
84             row_disabled = True
85         status_item = TransactionItem(status)
86         self.setItem(0, 0, status_item)
87
88         date_formatter = QDateTime()
89         date_formatter.setTime_t(unixtime)
90         # we need to do this in parts to have a month name translation
91         # datetime = date_formatter.toString('hh:mm d ')
92         # datetime += self.tr(date_formatter.toString('MMM '))
93         # datetime += date_formatter.toString('yy')
94         datetime = date_formatter.toString('hh:mm d MMM yy')
95         date_item = TransactionItem(datetime)
96         self.setItem(0, 1, date_item)
97
98         if category == 'send':
99             description = self.tr('Sent to %s')%address
100         elif category == 'receive':
101             description = self.tr('Received to %s')%address
102         trans_item = TransactionItem(description)
103         self.setItem(0, 2, trans_item)
104
105         credits_item = TransactionItem(str(credit), Qt.AlignRight)
106         self.setItem(0, 3, credits_item)
107
108         balance_item = TransactionItem(str(balance), Qt.AlignRight)
109         self.setItem(0, 4, balance_item)
110
111         if row_disabled:
112             self.disable_table_item(status_item)
113             self.disable_table_item(date_item)
114             self.disable_table_item(trans_item)
115             self.disable_table_item(credits_item)
116             self.disable_table_item(balance_item)
117
118     def disable_table_item(self, item):
119         brush = item.foreground()
120         brush.setColor(Qt.gray)
121         item.setForeground(brush)
122         font = item.font()
123         font.setStyle(font.StyleItalic)
124         item.setFont(font)
125
126 class Cashier(QDialog):
127     def __init__(self, core, clipboard, parent=None):
128         super(Cashier, self).__init__(parent)
129         self.core = core
130         self.clipboard = clipboard
131
132         self.create_actions()
133         main_layout = QVBoxLayout(self)
134
135         youraddy = QHBoxLayout()
136         # Balance + Send button
137         self.balance_label = QLabel()
138         self.refresh_balance()
139         sendbtn = QToolButton(self)
140         sendbtn.setDefaultAction(self.send_act)
141         # Address + New button + Copy button
142         uraddtext = QLabel(self.tr('Your address:'))
143         self.addy = FocusLineEdit(self.core.default_address())
144         newaddybtn = QToolButton(self)
145         newaddybtn.setDefaultAction(self.newaddy_act)
146         copyaddybtn = QToolButton(self)
147         copyaddybtn.setDefaultAction(self.copyaddy_act)
148         # Add them to the layout
149         youraddy.addWidget(uraddtext)
150         youraddy.addWidget(self.addy)
151         youraddy.addWidget(newaddybtn)
152         youraddy.addWidget(copyaddybtn)
153         youraddy.addStretch()
154         youraddy.addWidget(self.balance_label)
155         youraddy.addWidget(sendbtn)
156         main_layout.addLayout(youraddy)
157
158         self.transactions_table = TransactionsTable()
159         main_layout.addWidget(self.transactions_table)
160
161         #webview = QWebView()
162         #webview.load('http://bitcoinwatch.com/')
163         #webview.setFixedSize(880, 300)
164         #mf = webview.page().mainFrame()
165         #mf.setScrollBarPolicy(Qt.Horizontal,
166         #                      Qt.ScrollBarAlwaysOff)
167         #mf.setScrollBarPolicy(Qt.Vertical,
168         #                      Qt.ScrollBarAlwaysOff)
169         #main_layout.addWidget(webview)
170
171         self.setWindowTitle(self.tr('Spesmilo'))
172         if parent is not None:
173             self.setWindowIcon(parent.bitcoin_icon)
174         self.setAttribute(Qt.WA_DeleteOnClose, False)
175
176         refresh_info_timer = QTimer(self)
177         refresh_info_timer.timeout.connect(self.refresh_info)
178         refresh_info_timer.start(1000)
179         # Stores time of last transaction added to the table
180         self.last_tx_time = 0
181         # Used for updating number of confirms
182         #   key=txid, category  val=row, confirms
183         self.trans_lookup = {}
184         self.refresh_info()
185         #self.transactions_table.add_transaction_entry({'confirmations': 3, 'time': 1223332, 'address': 'fake', 'amount': 111, 'category': 'send'})
186         #self.transactions_table.add_transaction_entry({'confirmations': 0, 'time': 1223332, 'address': 'fake', 'amount': 111, 'category': 'send'})
187
188         self.resize(900, 300)
189
190     def refresh_info(self):
191         self.refresh_balance()
192         self.refresh_transactions()
193
194     def refresh_transactions(self):
195         #transactions = [t for t in self.core.transactions() if t['time'] > self.last_tx_time]
196         transactions = self.core.transactions()
197         self.transactions_table.clearContents()
198         self.transactions_table.setRowCount(0)
199         # whether list has updates
200         #if not transactions:
201         #    return
202         #self.last_tx_time = transactions[-1]['time']
203         transactions.sort(key=lambda t: t['time'])
204
205         #txids = [t['txid'] for t in transactions]
206         # remove duplicates
207         #txids = list(set(txids))
208         #for txid in txids:
209         #    mattrans = [t for t in transactions if t['txid'] == txid]
210
211         for t in transactions:
212             self.transactions_table.add_transaction_entry(t)
213
214     def refresh_balance(self):
215         bltext = self.tr('Balance: %.2f BTC')%self.core.balance()
216         self.balance_label.setText(bltext)
217
218     def create_actions(self):
219         icon = lambda s: QIcon('./icons/' + s)
220
221         self.send_act = QAction(icon('forward.png'), self.tr('Send'),
222             self, toolTip=self.tr('Send bitcoins to another person'),
223             triggered=self.new_send_dialog)
224         self.newaddy_act = QAction(icon('document_new.png'),
225             self.tr('New address'), self,
226             toolTip=self.tr('Create new address for accepting bitcoins'),
227             triggered=self.new_address)
228         self.copyaddy_act = QAction(icon('klipper.png'),
229             self.tr('Copy address'),
230             self, toolTip=self.tr('Copy address to clipboard'),
231             triggered=self.copy_address)
232
233     def new_send_dialog(self):
234         if self.parent() is not None:
235             send_dialog = send.SendDialog(self.core, self.parent())
236         else:
237             send_dialog = send.SendDialog(self.core, self)
238
239     def new_address(self):
240         self.addy.setText(self.core.new_address())
241
242     def copy_address(self):
243         self.clipboard.setText(self.addy.text())
244
245 if __name__ == '__main__':
246     import os
247     import sys
248     import core_interface
249     os.system('/home/genjix/src/bitcoin/bitcoind')
250     translator = QTranslator()
251     #translator.load('data/translations/eo_EO')
252     app = QApplication(sys.argv)
253     core = core_interface.CoreInterface()
254     clipboard = qApp.clipboard()
255     cashier = Cashier(core, clipboard)
256     cashier.show()
257     sys.exit(app.exec_())