1 from PyQt4.QtCore import *
2 from PyQt4.QtGui import *
9 def IconButton(filename, parent=None):
10 pixmap = QPixmap(filename)
12 return QPushButton(icon, "", parent)
16 def __init__(self, wallet):
18 self.app = QApplication(sys.argv)
19 with open("data/style.css") as style_file:
20 self.app.setStyleSheet(style_file.read())
23 actuator = MiniActuator(self.wallet)
24 mini = MiniWindow(actuator)
25 driver = MiniDriver(self.wallet, mini)
26 sys.exit(self.app.exec_())
28 class MiniWindow(QDialog):
30 def __init__(self, actuator):
31 super(MiniWindow, self).__init__()
33 self.actuator = actuator
35 accounts_button = IconButton("data/icons/accounts.png")
36 accounts_button.setObjectName("accounts_button")
38 self.accounts_selector = QMenu()
39 accounts_button.setMenu(self.accounts_selector)
41 interact_button = IconButton("data/icons/interact.png")
42 interact_button.setObjectName("interact_button")
45 interact_button.setMenu(app_menu)
47 expand_button = IconButton("data/icons/expand.png")
48 expand_button.setObjectName("expand_button")
50 self.balance_label = BalanceLabel()
51 self.balance_label.setObjectName("balance_label")
53 copy_button = QPushButton(_("&Copy Address"))
54 copy_button.setObjectName("copy_button")
55 copy_button.setDefault(True)
56 self.connect(copy_button, SIGNAL("clicked()"),
57 self.actuator.copy_address)
60 self.address_input = TextedLineEdit(_("Enter a Bitcoin address..."))
61 self.address_input.setObjectName("address_input")
62 self.connect(self.address_input, SIGNAL("textChanged(QString)"),
63 self.address_field_changed)
64 metrics = QFontMetrics(qApp.font())
65 self.address_input.setMinimumWidth(
66 metrics.width("1E4vM9q25xsyDwWwdqHUWnwshdWC9PykmL"))
68 self.valid_address = QCheckBox()
69 self.valid_address.setObjectName("valid_address")
70 self.valid_address.setEnabled(False)
71 self.valid_address.setChecked(False)
73 address_layout = QHBoxLayout()
74 address_layout.addWidget(self.address_input)
75 address_layout.addWidget(self.valid_address)
77 self.amount_input = TextedLineEdit(_("... and amount"))
78 self.amount_input.setObjectName("amount_input")
79 # This is changed according to the user's displayed balance
80 self.amount_validator = QDoubleValidator(self.amount_input)
81 self.amount_validator.setNotation(QDoubleValidator.StandardNotation)
82 self.amount_validator.setRange(0, 0)
83 self.amount_validator.setDecimals(2)
84 self.amount_input.setValidator(self.amount_validator)
86 amount_layout = QHBoxLayout()
87 amount_layout.addWidget(self.amount_input)
88 amount_layout.addStretch()
90 send_button = QPushButton(_("&Send"))
91 send_button.setObjectName("send_button")
92 self.connect(send_button, SIGNAL("clicked()"), self.send)
94 main_layout = QGridLayout(self)
95 main_layout.addWidget(accounts_button, 0, 0)
96 main_layout.addWidget(interact_button, 1, 0)
97 main_layout.addWidget(expand_button, 2, 0)
99 main_layout.addWidget(self.balance_label, 0, 1)
100 main_layout.addWidget(copy_button, 0, 2)
102 main_layout.addLayout(address_layout, 1, 1, 1, -1)
104 main_layout.addLayout(amount_layout, 2, 1)
105 main_layout.addWidget(send_button, 2, 2)
107 self.setWindowTitle("Electrum")
108 self.setWindowFlags(Qt.Window|Qt.MSWindowsFixedSizeDialogHint)
109 self.layout().setSizeConstraint(QLayout.SetFixedSize)
110 self.setObjectName("main_window")
113 def closeEvent(self, event):
114 super(MiniWindow, self).closeEvent(event)
120 def deactivate(self):
123 def set_balances(self, btc_balance, quote_balance, quote_currency):
124 self.balance_label.set_balances( \
125 btc_balance, quote_balance, quote_currency)
126 self.amount_validator.setRange(0, btc_balance)
127 main_account_info = \
128 "Checking - %s BTC (%s %s)" % (btc_balance,
129 quote_balance, quote_currency)
130 self.setWindowTitle("Electrum - %s" % main_account_info)
131 self.accounts_selector.clear()
132 self.accounts_selector.addAction("%s" % main_account_info)
135 self.actuator.send(self.address_input.text(),
136 self.amount_input.text(), self)
138 def address_field_changed(self, address):
139 if self.actuator.is_valid(address):
140 self.valid_address.setChecked(True)
142 self.valid_address.setChecked(False)
144 class BalanceLabel(QLabel):
146 def __init__(self, parent=None):
147 super(QLabel, self).__init__("Connecting...", parent)
149 def set_balances(self, btc_balance, quote_balance, quote_currency):
150 label_text = "<span style='font-size: 16pt'>%s</span> <span style='font-size: 10pt'>BTC</span> <span style='font-size: 10pt'>(%s %s)</span>" % (btc_balance, quote_balance, quote_currency)
151 self.setText(label_text)
153 class TextedLineEdit(QLineEdit):
155 def __init__(self, inactive_text, parent=None):
156 super(QLineEdit, self).__init__(parent)
157 self.inactive_text = inactive_text
158 self.become_inactive()
160 def mousePressEvent(self, event):
161 if self.isReadOnly():
163 QLineEdit.mousePressEvent(self, event)
165 def focusOutEvent(self, event):
166 if self.text() == "":
167 self.become_inactive()
168 QLineEdit.focusOutEvent(self, event)
170 def focusInEvent(self, event):
171 if self.isReadOnly():
173 QLineEdit.focusInEvent(self, event)
175 def become_inactive(self):
176 self.setText(self.inactive_text)
177 self.setReadOnly(True)
178 self.recompute_style()
180 def become_active(self):
182 self.setReadOnly(False)
183 self.recompute_style()
185 def recompute_style(self):
186 qApp.style().unpolish(self)
187 qApp.style().polish(self)
188 # also possible but more expensive:
189 #qApp.setStyleSheet(qApp.styleSheet())
193 def __init__(self, wallet):
196 def copy_address(self):
197 addrs = [addr for addr in self.wallet.all_addresses()
198 if not self.wallet.is_change(addr)]
199 qApp.clipboard().setText(random.choice(addrs))
201 def send(self, address, amount, parent_window):
202 recipient = unicode(address).strip()
205 match1 = re.match(ALIAS_REGEXP, r)
206 # label or alias, with address in brackets
207 match2 = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
211 self.wallet.get_alias(recipient, True,
212 self.show_message, self.question)
216 dest_address = match2.group(2)
218 dest_address = recipient
220 if not self.wallet.is_valid(dest_address):
221 QMessageBox.warning(parent_window, _('Error'),
222 _('Invalid Bitcoin Address') + ':\n' + dest_address, _('OK'))
225 convert_amount = lambda amount: \
226 int(decimal.Decimal(unicode(amount)) * 100000000)
228 amount = convert_amount(amount)
230 if self.wallet.use_encryption:
231 password = self.password_dialog()
238 tx = self.wallet.mktx(dest_address, amount, "", password, fee)
239 except BaseException, e:
240 self.show_message(str(e))
243 status, msg = self.wallet.sendtx( tx )
245 QMessageBox.information(self, '', _('Payment sent.')+'\n'+msg, _('OK'))
247 self.update_contacts_tab()
249 QMessageBox.warning(self, _('Error'), msg, _('OK'))
251 def is_valid(self, address):
252 return self.wallet.is_valid(address)
254 class MiniDriver(QObject):
261 def __init__(self, wallet, window):
262 super(QObject, self).__init__()
267 self.wallet.gui_callback = self.update_callback
272 self.connect(self, SIGNAL("updatesignal()"), self.update)
274 # This is a hack to workaround that Qt does not like changing the
275 # window properties from this other thread before the runloop has
277 def update_callback(self):
278 self.emit(SIGNAL("updatesignal()"))
281 if not self.wallet.interface:
283 elif not self.wallet.interface.is_connected:
285 elif not self.wallet.blocks == -1:
287 elif not self.wallet.is_up_to_date:
292 if self.wallet.up_to_date:
293 self.update_balance()
295 def initializing(self):
296 if self.state == self.INITIALIZING:
298 self.state = self.INITIALIZING
299 self.window.deactivate()
301 def connecting(self):
302 if self.state == self.CONNECTING:
304 self.state = self.CONNECTING
305 self.window.deactivate()
307 def synchronizing(self):
308 if self.state == self.SYNCHRONIZING:
310 self.state = self.SYNCHRONIZING
311 self.window.deactivate()
314 if self.state == self.READY:
316 self.state = self.READY
317 self.window.activate()
319 def update_balance(self):
320 conf_balance, unconf_balance = self.wallet.get_balance()
321 balance = conf_balance if unconf_balance is None else unconf_balance
322 self.window.set_balances(balance, balance * 6, 'EUR')
324 if __name__ == "__main__":
325 app = QApplication(sys.argv)
326 with open("data/style.css") as style_file:
327 app.setStyleSheet(style_file.read())
329 sys.exit(app.exec_())