3 # Let's do some dep checking and handle missing ones gracefully
5 from PyQt4.QtCore import *
6 from PyQt4.QtGui import *
7 import PyQt4.QtCore as QtCore
10 print "You need to have PyQT installed to run Electrum in graphical mode."
11 print "If you have pip installed try 'sudo pip install pyqt' if you are on Debian/Ubuntu try 'sudo apt-get install python-qt4'."
17 from decimal import Decimal as D
18 from util import get_resource_path as rsrc
34 bitcoin = lambda v: v * 100000000
36 def IconButton(filename, parent=None):
37 pixmap = QPixmap(filename)
39 return QPushButton(icon, "", parent)
44 self.emit(SIGNAL('timersignal'))
47 def resize_line_edit_width(line_edit, text_input):
48 metrics = QFontMetrics(qApp.font())
49 # Create an extra character to add some space on the end
51 line_edit.setMinimumWidth(metrics.width(text_input))
53 def load_theme_name(theme_path):
55 with open(os.path.join(theme_path, "name.cfg")) as name_cfg_file:
56 return name_cfg_file.read().rstrip("\n").strip()
61 def theme_dirs_from_prefix(prefix):
62 if not os.path.exists(prefix):
65 for potential_theme in os.listdir(prefix):
66 theme_full_path = os.path.join(prefix, potential_theme)
67 theme_css = os.path.join(theme_full_path, "style.css")
68 if not os.path.exists(theme_css):
70 theme_name = load_theme_name(theme_full_path)
71 if theme_name is None:
73 theme_paths[theme_name] = prefix, potential_theme
76 def load_theme_paths():
78 prefixes = (util.local_data_dir(), util.appdata_dir())
79 for prefix in prefixes:
80 theme_paths.update(theme_dirs_from_prefix(prefix))
84 class ElectrumGui(QObject):
86 def __init__(self, wallet, config):
87 super(QObject, self).__init__()
91 self.check_qt_version()
92 self.app = QApplication(sys.argv)
95 def check_qt_version(self):
96 qtVersion = qVersion()
97 if not(int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7):
98 app = QApplication(sys.argv)
99 QMessageBox.warning(None,"Could not start Lite GUI.", "Electrum was unable to load the 'Lite GUI' because it needs Qt version >= 4.7.\nChanging your config to use the 'Classic' GUI")
100 self.config.set_key('gui','classic',True)
105 actuator = MiniActuator(self.wallet)
106 # Should probably not modify the current path but instead
107 # change the behaviour of rsrc(...)
108 old_path = QDir.currentPath()
109 actuator.load_theme()
111 self.mini = MiniWindow(actuator, self.expand, self.config)
112 driver = MiniDriver(self.wallet, self.mini)
114 # Reset path back to original value now that loading the GUI
116 QDir.setCurrent(old_path)
123 self.expert = gui_qt.ElectrumWindow(self.wallet, self.config)
124 self.expert.app = self.app
125 self.expert.connect_slots(timer)
126 self.expert.update_wallet()
130 """Hide the lite mode window and show pro-mode."""
134 def set_url(self, url):
135 payto, amount, label, message, signature, identity, url = \
136 self.wallet.parse_url(url, self.show_message, self.show_question)
137 self.mini.set_payment_fields(payto, amount)
139 def show_message(self, message):
140 QMessageBox.information(self.mini, _("Message"), message, _("OK"))
142 def show_question(self, message):
143 choice = QMessageBox.question(self.mini, _("Message"), message,
144 QMessageBox.Yes|QMessageBox.No,
146 return choice == QMessageBox.Yes
148 def restore_or_create(self):
149 qt_gui_object = gui_qt.ElectrumGui(self.wallet, self.app)
150 return qt_gui_object.restore_or_create()
152 class MiniWindow(QDialog):
154 def __init__(self, actuator, expand_callback, config):
155 super(MiniWindow, self).__init__()
157 self.actuator = actuator
160 self.btc_balance = None
161 self.quote_currencies = ["EUR", "USD", "GBP"]
162 self.actuator.set_configured_currency(self.set_quote_currency)
163 self.exchanger = exchange_rate.Exchanger(self)
164 # Needed because price discovery is done in a different thread
165 # which needs to be sent back to this main one to update the GUI
166 self.connect(self, SIGNAL("refresh_balance()"), self.refresh_balance)
168 self.balance_label = BalanceLabel(self.change_quote_currency)
169 self.balance_label.setObjectName("balance_label")
171 self.receive_button = QPushButton(_("&Receive"))
172 self.receive_button.setObjectName("receive_button")
173 self.receive_button.setDefault(True)
174 self.receive_button.clicked.connect(self.copy_address)
176 # Bitcoin address code
177 self.address_input = QLineEdit()
178 self.address_input.setPlaceholderText(_("Enter a Bitcoin address..."))
179 self.address_input.setObjectName("address_input")
182 self.address_input.textEdited.connect(self.address_field_changed)
183 resize_line_edit_width(self.address_input,
184 "1BtaFUr3qVvAmwrsuDuu5zk6e4s2rxd2Gy")
186 self.address_completions = QStringListModel()
187 address_completer = QCompleter(self.address_input)
188 address_completer.setCaseSensitivity(False)
189 address_completer.setModel(self.address_completions)
190 self.address_input.setCompleter(address_completer)
192 address_layout = QHBoxLayout()
193 address_layout.addWidget(self.address_input)
195 self.amount_input = QLineEdit()
196 self.amount_input.setPlaceholderText(_("... and amount"))
197 self.amount_input.setObjectName("amount_input")
198 # This is changed according to the user's displayed balance
199 self.amount_validator = QDoubleValidator(self.amount_input)
200 self.amount_validator.setNotation(QDoubleValidator.StandardNotation)
201 self.amount_validator.setDecimals(8)
202 self.amount_input.setValidator(self.amount_validator)
204 # This removes the very ugly OSX highlighting, please leave this in :D
205 self.address_input.setAttribute(Qt.WA_MacShowFocusRect, 0)
206 self.amount_input.setAttribute(Qt.WA_MacShowFocusRect, 0)
207 self.amount_input.textChanged.connect(self.amount_input_changed)
209 self.send_button = QPushButton(_("&Send"))
210 self.send_button.setObjectName("send_button")
211 self.send_button.setDisabled(True);
212 self.send_button.clicked.connect(self.send)
214 main_layout = QGridLayout(self)
216 main_layout.addWidget(self.balance_label, 0, 0)
217 main_layout.addWidget(self.receive_button, 0, 1)
219 main_layout.addWidget(self.address_input, 1, 0, 1, -1)
221 main_layout.addWidget(self.amount_input, 2, 0)
222 main_layout.addWidget(self.send_button, 2, 1)
224 self.history_list = history_widget.HistoryWidget()
225 self.history_list.setObjectName("history")
226 self.history_list.hide()
227 self.history_list.setAlternatingRowColors(True)
228 main_layout.addWidget(self.history_list, 3, 0, 1, -1)
231 electrum_menu = menubar.addMenu(_("&Bitcoin"))
233 electrum_menu.addSeparator()
235 brain_seed = electrum_menu.addAction(_("&BrainWallet Info"))
236 brain_seed.triggered.connect(self.actuator.show_seed_dialog)
237 quit_option = electrum_menu.addAction(_("&Quit"))
238 quit_option.triggered.connect(self.close)
240 view_menu = menubar.addMenu(_("&View"))
241 extra_menu = menubar.addMenu(_("&Extra"))
243 backup_wallet = extra_menu.addAction( _("&Create wallet backup"))
244 backup_wallet.triggered.connect(self.backup_wallet)
246 expert_gui = view_menu.addAction(_("&Classic GUI"))
247 expert_gui.triggered.connect(expand_callback)
248 themes_menu = view_menu.addMenu(_("&Themes"))
249 selected_theme = self.actuator.selected_theme()
250 theme_group = QActionGroup(self)
251 for theme_name in self.actuator.theme_names():
252 theme_action = themes_menu.addAction(theme_name)
253 theme_action.setCheckable(True)
254 if selected_theme == theme_name:
255 theme_action.setChecked(True)
256 class SelectThemeFunctor:
257 def __init__(self, theme_name, toggle_theme):
258 self.theme_name = theme_name
259 self.toggle_theme = toggle_theme
260 def __call__(self, checked):
262 self.toggle_theme(self.theme_name)
263 delegate = SelectThemeFunctor(theme_name, self.toggle_theme)
264 theme_action.toggled.connect(delegate)
265 theme_group.addAction(theme_action)
266 view_menu.addSeparator()
267 show_history = view_menu.addAction(_("Show History"))
268 show_history.setCheckable(True)
269 show_history.toggled.connect(self.show_history)
271 help_menu = menubar.addMenu(_("&Help"))
272 the_website = help_menu.addAction(_("&Website"))
273 the_website.triggered.connect(self.the_website)
274 help_menu.addSeparator()
275 report_bug = help_menu.addAction(_("&Report Bug"))
276 report_bug.triggered.connect(self.show_report_bug)
277 show_about = help_menu.addAction(_("&About"))
278 show_about.triggered.connect(self.show_about)
279 main_layout.setMenuBar(menubar)
281 quit_shortcut = QShortcut(QKeySequence("Ctrl+Q"), self)
282 quit_shortcut.activated.connect(self.close)
283 close_shortcut = QShortcut(QKeySequence("Ctrl+W"), self)
284 close_shortcut.activated.connect(self.close)
286 g = self.config.get("winpos-lite",[4, 25, 351, 149])
287 self.setGeometry(g[0], g[1], g[2], g[3])
289 show_hist = self.config.get("gui_show_history",False)
290 show_history.setChecked(show_hist)
291 self.show_history(show_hist)
293 self.setWindowIcon(QIcon(":electrum.png"))
294 self.setWindowTitle("Electrum")
295 self.setWindowFlags(Qt.Window|Qt.MSWindowsFixedSizeDialogHint)
296 self.layout().setSizeConstraint(QLayout.SetFixedSize)
297 self.setObjectName("main_window")
300 def toggle_theme(self, theme_name):
301 old_path = QDir.currentPath()
302 self.actuator.change_theme(theme_name)
303 # Recompute style globally
304 qApp.style().unpolish(self)
305 qApp.style().polish(self)
306 QDir.setCurrent(old_path)
308 def closeEvent(self, event):
310 self.config.set_key("winpos-lite", [g.left(),g.top(),g.width(),g.height()],True)
311 self.config.set_key("gui_show_history", self.history_list.isVisible(),True)
313 super(MiniWindow, self).closeEvent(event)
316 def set_payment_fields(self, dest_address, amount):
317 self.address_input.setText(dest_address)
318 self.address_field_changed(dest_address)
319 self.amount_input.setText(amount)
324 def deactivate(self):
327 def set_quote_currency(self, currency):
328 """Set and display the fiat currency country."""
329 assert currency in self.quote_currencies
330 self.quote_currencies.remove(currency)
331 self.quote_currencies.insert(0, currency)
332 self.refresh_balance()
334 def change_quote_currency(self):
335 self.quote_currencies = \
336 self.quote_currencies[1:] + self.quote_currencies[0:1]
337 self.actuator.set_config_currency(self.quote_currencies[0])
338 self.refresh_balance()
340 def refresh_balance(self):
341 if self.btc_balance is None:
342 # Price has been discovered before wallet has been loaded
343 # and server connect... so bail.
345 self.set_balances(self.btc_balance)
346 self.amount_input_changed(self.amount_input.text())
348 def set_balances(self, btc_balance):
349 """Set the bitcoin balance and update the amount label accordingly."""
350 self.btc_balance = btc_balance
351 quote_text = self.create_quote_text(btc_balance)
353 quote_text = "(%s)" % quote_text
354 btc_balance = "%.2f" % (btc_balance / bitcoin(1))
355 self.balance_label.set_balance_text(btc_balance, quote_text)
356 self.setWindowTitle("Electrum - %s BTC" % btc_balance)
358 def amount_input_changed(self, amount_text):
359 """Update the number of bitcoins displayed."""
360 self.check_button_status()
363 amount = D(str(amount_text))
364 except decimal.InvalidOperation:
365 self.balance_label.show_balance()
367 quote_text = self.create_quote_text(amount * bitcoin(1))
369 self.balance_label.set_amount_text(quote_text)
370 self.balance_label.show_amount()
372 self.balance_label.show_balance()
374 def create_quote_text(self, btc_balance):
375 """Return a string copy of the amount fiat currency the
376 user has in bitcoins."""
377 quote_currency = self.quote_currencies[0]
378 quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
379 if quote_balance is None:
382 quote_text = "%.2f %s" % ((quote_balance / bitcoin(1)),
387 if self.actuator.send(self.address_input.text(),
388 self.amount_input.text(), self):
389 self.address_input.setText("")
390 self.amount_input.setText("")
392 def check_button_status(self):
393 """Check that the bitcoin address is valid and that something
394 is entered in the amount before making the send button clickable."""
396 value = D(str(self.amount_input.text())) * 10**8
397 except decimal.InvalidOperation:
399 # self.address_input.property(...) returns a qVariant, not a bool.
400 # The == is needed to properly invoke a comparison.
401 if (self.address_input.property("isValid") == True and
402 value is not None and 0 < value <= self.btc_balance):
403 self.send_button.setDisabled(False)
405 self.send_button.setDisabled(True)
407 def address_field_changed(self, address):
408 if self.actuator.is_valid(address):
409 self.check_button_status()
410 self.address_input.setProperty("isValid", True)
411 self.recompute_style(self.address_input)
413 self.send_button.setDisabled(True)
414 self.address_input.setProperty("isValid", False)
415 self.recompute_style(self.address_input)
417 if len(address) == 0:
418 self.address_input.setProperty("isValid", None)
419 self.recompute_style(self.address_input)
421 def recompute_style(self, element):
422 self.style().unpolish(element)
423 self.style().polish(element)
425 def copy_address(self):
426 receive_popup = ReceivePopup(self.receive_button)
427 self.actuator.copy_address(receive_popup)
429 def update_completions(self, completions):
430 self.address_completions.setStringList(completions)
432 def update_history(self, tx_history):
433 from util import format_satoshis
434 for item in tx_history[-10:]:
435 tx_hash, conf, is_mine, value, fee, balance, timestamp = item
436 label = self.actuator.wallet.get_label(tx_hash)[0]
437 #amount = D(value) / 10**8
438 v_str = format_satoshis(value, True)
439 self.history_list.append(label, v_str)
442 self.actuator.acceptbit(self.quote_currencies[0])
444 def the_website(self):
445 webbrowser.open("http://electrum-desktop.com")
447 def show_about(self):
448 QMessageBox.about(self, "Electrum",
449 _("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.\n\nSend donations to 1JwTMv4GWaPdf931N6LNPJeZBfZgZJ3zX1"))
451 def show_report_bug(self):
452 QMessageBox.information(self, "Electrum - " + _("Reporting Bugs"),
453 _("Email bug reports to %s") % "genjix" + "@" + "riseup.net")
455 def show_history(self, toggle_state):
457 self.history_list.show()
459 self.history_list.hide()
461 def backup_wallet(self):
463 folderName = QFileDialog.getExistingDirectory(QWidget(), 'Select folder to save a copy of your wallet to', os.path.expanduser('~/'))
465 sourceFile = util.user_dir() + '/electrum.dat'
466 shutil.copy2(sourceFile, str(folderName))
467 QMessageBox.information(None,"Wallet backup created", "A copy of your wallet file was created in '%s'" % str(folderName))
468 except (IOError, os.error), reason:
469 QMessageBox.critical(None,"Unable to create backup", "Electrum was unable copy your wallet file to the specified location.\n" + str(reason))
474 class BalanceLabel(QLabel):
480 def __init__(self, change_quote_currency, parent=None):
481 super(QLabel, self).__init__(_("Connecting..."), parent)
482 self.change_quote_currency = change_quote_currency
483 self.state = self.SHOW_CONNECTING
484 self.balance_text = ""
485 self.amount_text = ""
487 def mousePressEvent(self, event):
488 """Change the fiat currency selection if window background is clicked."""
489 if self.state != self.SHOW_CONNECTING:
490 self.change_quote_currency()
492 def set_balance_text(self, btc_balance, quote_text):
493 """Set the amount of bitcoins in the gui."""
494 if self.state == self.SHOW_CONNECTING:
495 self.state = self.SHOW_BALANCE
496 self.balance_text = "<span style='font-size: 18pt'>%s</span> <span style='font-size: 10pt'>BTC</span> <span style='font-size: 10pt'>%s</span>" % (btc_balance, quote_text)
497 if self.state == self.SHOW_BALANCE:
498 self.setText(self.balance_text)
500 def set_amount_text(self, quote_text):
501 self.amount_text = "<span style='font-size: 10pt'>%s</span>" % quote_text
502 if self.state == self.SHOW_AMOUNT:
503 self.setText(self.amount_text)
505 def show_balance(self):
506 if self.state == self.SHOW_AMOUNT:
507 self.state = self.SHOW_BALANCE
508 self.setText(self.balance_text)
510 def show_amount(self):
511 if self.state == self.SHOW_BALANCE:
512 self.state = self.SHOW_AMOUNT
513 self.setText(self.amount_text)
515 def ok_cancel_buttons(dialog):
516 row_layout = QHBoxLayout()
517 row_layout.addStretch(1)
518 ok_button = QPushButton(_("OK"))
519 row_layout.addWidget(ok_button)
520 ok_button.clicked.connect(dialog.accept)
521 cancel_button = QPushButton(_("Cancel"))
522 row_layout.addWidget(cancel_button)
523 cancel_button.clicked.connect(dialog.reject)
526 class PasswordDialog(QDialog):
528 def __init__(self, parent):
529 super(QDialog, self).__init__(parent)
533 self.password_input = QLineEdit()
534 self.password_input.setEchoMode(QLineEdit.Password)
536 main_layout = QVBoxLayout(self)
537 message = _('Please enter your password')
538 main_layout.addWidget(QLabel(message))
542 grid.addWidget(QLabel(_('Password')), 1, 0)
543 grid.addWidget(self.password_input, 1, 1)
544 main_layout.addLayout(grid)
546 main_layout.addLayout(ok_cancel_buttons(self))
547 self.setLayout(main_layout)
552 return unicode(self.password_input.text())
554 class ReceivePopup(QDialog):
556 def leaveEvent(self, event):
559 def setup(self, address):
560 label = QLabel(_("Copied your Bitcoin address to the clipboard!"))
561 address_display = QLineEdit(address)
562 address_display.setReadOnly(True)
563 resize_line_edit_width(address_display, address)
565 main_layout = QVBoxLayout(self)
566 main_layout.addWidget(label)
567 main_layout.addWidget(address_display)
569 self.setMouseTracking(True)
570 self.setWindowTitle("Electrum - " + _("Receive Bitcoin payment"))
571 self.setWindowFlags(Qt.Window|Qt.FramelessWindowHint|
572 Qt.MSWindowsFixedSizeDialogHint)
573 self.layout().setSizeConstraint(QLayout.SetFixedSize)
574 #self.setFrameStyle(QFrame.WinPanel|QFrame.Raised)
575 #self.setAlignment(Qt.AlignCenter)
578 parent = self.parent()
579 top_left_pos = parent.mapToGlobal(parent.rect().bottomLeft())
580 self.move(top_left_pos)
581 center_mouse_pos = self.mapToGlobal(self.rect().center())
582 QCursor.setPos(center_mouse_pos)
586 """Initialize the definitions relating to themes and
587 sending/recieving bitcoins."""
590 def __init__(self, wallet):
591 """Retrieve the gui theme used in previous session."""
593 self.theme_name = self.wallet.config.get('litegui_theme','Cleanlook')
594 self.themes = load_theme_paths()
596 def load_theme(self):
597 """Load theme retrieved from wallet file."""
599 theme_prefix, theme_path = self.themes[self.theme_name]
601 util.print_error("Theme not found!", self.theme_name)
603 QDir.setCurrent(os.path.join(theme_prefix, theme_path))
604 with open(rsrc("style.css")) as style_file:
605 qApp.setStyleSheet(style_file.read())
607 def theme_names(self):
609 return sorted(self.themes.keys())
611 def selected_theme(self):
613 return self.theme_name
615 def change_theme(self, theme_name):
617 self.theme_name = theme_name
618 self.wallet.config.set_key('litegui_theme',theme_name)
621 def set_configured_currency(self, set_quote_currency):
622 """Set the inital fiat currency conversion country (USD/EUR/GBP) in
623 the GUI to what it was set to in the wallet."""
624 currency = self.wallet.config.get('conversion_currency')
625 # currency can be none when Electrum is used for the first
626 # time and no setting has been created yet.
627 if currency is not None:
628 set_quote_currency(currency)
630 def set_config_currency(self, conversion_currency):
631 """Change the wallet fiat currency country."""
632 self.wallet.config.set_key('conversion_currency',conversion_currency,True)
634 def copy_address(self, receive_popup):
635 """Copy the wallet addresses into the client."""
636 addrs = [addr for addr in self.wallet.all_addresses()
637 if not self.wallet.is_change(addr)]
638 # Select most recent addresses from gap limit
639 addrs = addrs[-self.wallet.gap_limit:]
640 copied_address = random.choice(addrs)
641 qApp.clipboard().setText(copied_address)
642 receive_popup.setup(copied_address)
643 receive_popup.popup()
645 def waiting_dialog(self, f):
650 w.setWindowTitle('Electrum')
651 l = QLabel('Sending transaction, please wait.')
660 w.connect(s, QtCore.SIGNAL('timersignal'), ff)
664 def send(self, address, amount, parent_window):
665 """Send bitcoins to the target address."""
666 dest_address = self.fetch_destination(address)
668 if dest_address is None or not self.wallet.is_valid(dest_address):
669 QMessageBox.warning(parent_window, _('Error'),
670 _('Invalid Bitcoin Address') + ':\n' + address, _('OK'))
673 convert_amount = lambda amount: \
674 int(D(unicode(amount)) * bitcoin(1))
675 amount = convert_amount(amount)
677 if self.wallet.use_encryption:
678 password_dialog = PasswordDialog(parent_window)
679 password = password_dialog.run()
687 if amount < bitcoin(1) / 10:
689 fee = bitcoin(1) / 1000
692 tx = self.wallet.mktx([(dest_address, amount)], "", password, fee)
693 except BaseException as error:
694 QMessageBox.warning(parent_window, _('Error'), str(error), _('OK'))
697 h = self.wallet.send_tx(tx)
699 self.waiting_dialog(lambda: False if self.wallet.tx_event.isSet() else _("Sending transaction, please wait..."))
701 status, message = self.wallet.receive_tx(h)
705 dumpf = tempfile.NamedTemporaryFile(delete=False)
708 print "Dumped error tx to", dumpf.name
709 QMessageBox.warning(parent_window, _('Error'), message, _('OK'))
712 QMessageBox.information(parent_window, '',
713 _('Your transaction has been sent.') + '\n' + message, _('OK'))
716 def fetch_destination(self, address):
717 recipient = unicode(address).strip()
720 match1 = re.match("^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$",
723 # label or alias, with address in brackets
724 match2 = re.match("(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>",
729 self.wallet.get_alias(recipient, True,
730 self.show_message, self.question)
733 return match2.group(2)
737 def is_valid(self, address):
738 """Check if bitcoin address is valid."""
739 return self.wallet.is_valid(address)
741 def acceptbit(self, currency):
742 master_pubkey = self.wallet.master_public_key
743 url = "http://acceptbit.com/mpk/%s/%s" % (master_pubkey, currency)
746 def show_seed_dialog(self):
747 gui_qt.ElectrumWindow.show_seed_dialog(self.wallet)
749 class MiniDriver(QObject):
756 def __init__(self, wallet, window):
757 super(QObject, self).__init__()
762 self.wallet.interface.register_callback('updated',self.update_callback)
763 self.wallet.interface.register_callback('connected', self.update_callback)
764 self.wallet.interface.register_callback('disconnected', self.update_callback)
769 self.connect(self, SIGNAL("updatesignal()"), self.update)
770 self.update_callback()
772 # This is a hack to workaround that Qt does not like changing the
773 # window properties from this other thread before the runloop has
775 def update_callback(self):
776 self.emit(SIGNAL("updatesignal()"))
779 if not self.wallet.interface:
781 elif not self.wallet.interface.is_connected:
783 elif not self.wallet.up_to_date:
788 if self.wallet.up_to_date:
789 self.update_balance()
790 self.update_completions()
791 self.update_history()
793 def initializing(self):
794 if self.state == self.INITIALIZING:
796 self.state = self.INITIALIZING
797 self.window.deactivate()
799 def connecting(self):
800 if self.state == self.CONNECTING:
802 self.state = self.CONNECTING
803 self.window.deactivate()
805 def synchronizing(self):
806 if self.state == self.SYNCHRONIZING:
808 self.state = self.SYNCHRONIZING
809 self.window.deactivate()
812 if self.state == self.READY:
814 self.state = self.READY
815 self.window.activate()
817 def update_balance(self):
818 conf_balance, unconf_balance = self.wallet.get_balance()
819 balance = D(conf_balance + unconf_balance)
820 self.window.set_balances(balance)
822 def update_completions(self):
824 for addr, label in self.wallet.labels.items():
825 if addr in self.wallet.addressbook:
826 completions.append("%s <%s>" % (label, addr))
827 completions = completions + self.wallet.aliases.keys()
828 self.window.update_completions(completions)
830 def update_history(self):
831 tx_history = self.wallet.get_tx_history()
832 self.window.update_history(tx_history)
834 if __name__ == "__main__":
835 app = QApplication(sys.argv)
836 with open(rsrc("style.css")) as style_file:
837 app.setStyleSheet(style_file.read())
839 sys.exit(app.exec_())