1 from PyQt4.QtCore import *
2 from PyQt4.QtGui import *
10 import lib.gui_qt as gui_qt
12 import electrum.gui_qt as gui_qt
14 def IconButton(filename, parent=None):
15 pixmap = QPixmap(filename)
17 return QPushButton(icon, "", parent)
21 def __init__(self, wallet):
23 self.app = QApplication(sys.argv)
24 with open("data/style.css") as style_file:
25 self.app.setStyleSheet(style_file.read())
28 self.actuator = MiniActuator(self.wallet)
29 self.mini = MiniWindow(self.actuator, self.expand)
30 self.driver = MiniDriver(self.wallet, self.mini)
31 sys.exit(self.app.exec_())
34 self.wallet.gui_callback = None
39 #self.gui = gui_qt.ElectrumGui(self.wallet)
41 class MiniWindow(QDialog):
43 def __init__(self, actuator, expand_callback):
44 super(MiniWindow, self).__init__()
46 self.actuator = actuator
48 accounts_button = IconButton("data/icons/accounts.png")
49 accounts_button.setObjectName("accounts_button")
51 self.accounts_selector = QMenu()
52 accounts_button.setMenu(self.accounts_selector)
54 interact_button = IconButton("data/icons/interact.png")
55 interact_button.setObjectName("interact_button")
58 report_action = app_menu.addAction(_("&Report Bug"))
59 about_action = app_menu.addAction(_("&About Electrum"))
60 app_menu.addSeparator()
61 quit_action = app_menu.addAction(_("&Quit"))
62 interact_button.setMenu(app_menu)
64 self.connect(report_action, SIGNAL("triggered()"),
66 self.connect(about_action, SIGNAL("triggered()"), self.show_about)
67 self.connect(quit_action, SIGNAL("triggered()"), self.close)
69 expand_button = IconButton("data/icons/expand.png")
70 expand_button.setObjectName("expand_button")
71 self.connect(expand_button, SIGNAL("clicked()"), expand_callback)
73 self.balance_label = BalanceLabel()
74 self.balance_label.setObjectName("balance_label")
76 copy_button = QPushButton(_("&Copy Address"))
77 copy_button.setObjectName("copy_button")
78 copy_button.setDefault(True)
79 self.connect(copy_button, SIGNAL("clicked()"),
80 self.actuator.copy_address)
83 self.address_input = TextedLineEdit(_("Enter a Bitcoin address..."))
84 self.address_input.setObjectName("address_input")
85 self.connect(self.address_input, SIGNAL("textChanged(QString)"),
86 self.address_field_changed)
87 metrics = QFontMetrics(qApp.font())
88 self.address_input.setMinimumWidth(
89 metrics.width("1E4vM9q25xsyDwWwdqHUWnwshdWC9PykmL"))
91 self.valid_address = QCheckBox()
92 self.valid_address.setObjectName("valid_address")
93 self.valid_address.setEnabled(False)
94 self.valid_address.setChecked(False)
96 address_layout = QHBoxLayout()
97 address_layout.addWidget(self.address_input)
98 address_layout.addWidget(self.valid_address)
100 self.amount_input = TextedLineEdit(_("... and amount"))
101 self.amount_input.setObjectName("amount_input")
102 # This is changed according to the user's displayed balance
103 self.amount_validator = QDoubleValidator(self.amount_input)
104 self.amount_validator.setNotation(QDoubleValidator.StandardNotation)
105 self.amount_validator.setRange(0, 0)
106 self.amount_validator.setDecimals(2)
107 self.amount_input.setValidator(self.amount_validator)
109 amount_layout = QHBoxLayout()
110 amount_layout.addWidget(self.amount_input)
111 amount_layout.addStretch()
113 send_button = QPushButton(_("&Send"))
114 send_button.setObjectName("send_button")
115 self.connect(send_button, SIGNAL("clicked()"), self.send)
117 main_layout = QGridLayout(self)
118 main_layout.addWidget(accounts_button, 0, 0)
119 main_layout.addWidget(interact_button, 1, 0)
120 main_layout.addWidget(expand_button, 2, 0)
122 main_layout.addWidget(self.balance_label, 0, 1)
123 main_layout.addWidget(copy_button, 0, 2)
125 main_layout.addLayout(address_layout, 1, 1, 1, -1)
127 main_layout.addLayout(amount_layout, 2, 1)
128 main_layout.addWidget(send_button, 2, 2)
130 self.setWindowTitle("Electrum")
131 self.setWindowFlags(Qt.Window|Qt.MSWindowsFixedSizeDialogHint)
132 self.layout().setSizeConstraint(QLayout.SetFixedSize)
133 self.setObjectName("main_window")
136 def closeEvent(self, event):
137 super(MiniWindow, self).closeEvent(event)
143 def deactivate(self):
146 def set_balances(self, btc_balance, quote_balance, quote_currency):
147 self.balance_label.set_balances( \
148 btc_balance, quote_balance, quote_currency)
149 self.amount_validator.setRange(0, btc_balance)
150 main_account_info = \
151 "Checking - %s BTC (%s %s)" % (btc_balance,
152 quote_balance, quote_currency)
153 self.setWindowTitle("Electrum - %s" % main_account_info)
154 self.accounts_selector.clear()
155 self.accounts_selector.addAction("%s" % main_account_info)
158 self.actuator.send(self.address_input.text(),
159 self.amount_input.text(), self)
161 def address_field_changed(self, address):
162 if self.actuator.is_valid(address):
163 self.valid_address.setChecked(True)
165 self.valid_address.setChecked(False)
167 def show_about(self):
168 QMessageBox.about(self, "Electrum",
169 "Electrum's focus is speed, with low resource usage and simplifying Bitcoin. You do not need to perform regular backups, because your wallet can be recovered from a secret phrase that you can memorize or write on paper. Startup times are instant because it operates in conjuction with high-performance servers that handle the most complicated parts of the Bitcoin system.")
171 def show_report_bug(self):
172 QMessageBox.information(self, "Electrum - Reporting Bugs",
173 "Email bug reports to %s@%s.net" % ("genjix", "riseup"))
175 class BalanceLabel(QLabel):
177 def __init__(self, parent=None):
178 super(QLabel, self).__init__("Connecting...", parent)
180 def set_balances(self, btc_balance, quote_balance, quote_currency):
181 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)
182 self.setText(label_text)
184 class TextedLineEdit(QLineEdit):
186 def __init__(self, inactive_text, parent=None):
187 super(QLineEdit, self).__init__(parent)
188 self.inactive_text = inactive_text
189 self.become_inactive()
191 def mousePressEvent(self, event):
192 if self.isReadOnly():
194 QLineEdit.mousePressEvent(self, event)
196 def focusOutEvent(self, event):
197 if self.text() == "":
198 self.become_inactive()
199 QLineEdit.focusOutEvent(self, event)
201 def focusInEvent(self, event):
202 if self.isReadOnly():
204 QLineEdit.focusInEvent(self, event)
206 def become_inactive(self):
207 self.setText(self.inactive_text)
208 self.setReadOnly(True)
209 self.recompute_style()
211 def become_active(self):
213 self.setReadOnly(False)
214 self.recompute_style()
216 def recompute_style(self):
217 qApp.style().unpolish(self)
218 qApp.style().polish(self)
219 # also possible but more expensive:
220 #qApp.setStyleSheet(qApp.styleSheet())
224 def __init__(self, wallet):
227 def copy_address(self):
228 addrs = [addr for addr in self.wallet.all_addresses()
229 if not self.wallet.is_change(addr)]
230 qApp.clipboard().setText(random.choice(addrs))
232 def send(self, address, amount, parent_window):
233 recipient = unicode(address).strip()
236 match1 = re.match(ALIAS_REGEXP, r)
237 # label or alias, with address in brackets
238 match2 = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
242 self.wallet.get_alias(recipient, True,
243 self.show_message, self.question)
247 dest_address = match2.group(2)
249 dest_address = recipient
251 if not self.wallet.is_valid(dest_address):
252 QMessageBox.warning(parent_window, _('Error'),
253 _('Invalid Bitcoin Address') + ':\n' + dest_address, _('OK'))
256 convert_amount = lambda amount: \
257 int(decimal.Decimal(unicode(amount)) * 100000000)
259 amount = convert_amount(amount)
261 if self.wallet.use_encryption:
262 password = self.password_dialog()
269 tx = self.wallet.mktx(dest_address, amount, "", password, fee)
270 except BaseException, e:
271 self.show_message(str(e))
274 status, msg = self.wallet.sendtx( tx )
276 QMessageBox.information(self, '', _('Payment sent.')+'\n'+msg, _('OK'))
278 self.update_contacts_tab()
280 QMessageBox.warning(self, _('Error'), msg, _('OK'))
282 def is_valid(self, address):
283 return self.wallet.is_valid(address)
285 class MiniDriver(QObject):
292 def __init__(self, wallet, window):
293 super(QObject, self).__init__()
298 self.wallet.gui_callback = self.update_callback
303 self.connect(self, SIGNAL("updatesignal()"), self.update)
305 # This is a hack to workaround that Qt does not like changing the
306 # window properties from this other thread before the runloop has
308 def update_callback(self):
309 self.emit(SIGNAL("updatesignal()"))
312 if not self.wallet.interface:
314 elif not self.wallet.interface.is_connected:
316 elif not self.wallet.blocks == -1:
318 elif not self.wallet.is_up_to_date:
323 if self.wallet.up_to_date:
324 self.update_balance()
326 def initializing(self):
327 if self.state == self.INITIALIZING:
329 self.state = self.INITIALIZING
330 self.window.deactivate()
332 def connecting(self):
333 if self.state == self.CONNECTING:
335 self.state = self.CONNECTING
336 self.window.deactivate()
338 def synchronizing(self):
339 if self.state == self.SYNCHRONIZING:
341 self.state = self.SYNCHRONIZING
342 self.window.deactivate()
345 if self.state == self.READY:
347 self.state = self.READY
348 self.window.activate()
350 def update_balance(self):
351 conf_balance, unconf_balance = self.wallet.get_balance()
352 balance = conf_balance if unconf_balance is None else unconf_balance
353 self.window.set_balances(balance, balance * 6, 'EUR')
355 if __name__ == "__main__":
356 app = QApplication(sys.argv)
357 with open("data/style.css") as style_file:
358 app.setStyleSheet(style_file.read())
360 sys.exit(app.exec_())