3 # Let's do some dep checking and handle missing ones gracefully
5 from PyQt4.QtCore import *
6 from PyQt4.QtGui import *
7 from PyQt4.Qt import Qt
8 import PyQt4.QtCore as QtCore
11 print "You need to have PyQT installed to run Electrum in graphical mode."
12 print "If you have pip installed try 'sudo pip install pyqt' if you are on Debian/Ubuntu try 'sudo apt-get install python-qt4'."
15 from decimal import Decimal as D
16 from electrum.util import get_resource_path as rsrc
17 from electrum.bitcoin import is_valid
18 from electrum.i18n import _
25 from electrum.wallet import Wallet, WalletStorage
28 import receiving_widget
29 from electrum import util
32 from electrum.version import ELECTRUM_VERSION as electrum_version
33 from electrum.util import format_satoshis, age
35 from main_window import ElectrumWindow
40 bitcoin = lambda v: v * 100000000
42 def IconButton(filename, parent=None):
43 pixmap = QPixmap(filename)
45 return QPushButton(icon, "", parent)
48 def resize_line_edit_width(line_edit, text_input):
49 metrics = QFontMetrics(qApp.font())
50 # Create an extra character to add some space on the end
52 line_edit.setMinimumWidth(metrics.width(text_input))
54 def load_theme_name(theme_path):
56 with open(os.path.join(theme_path, "name.cfg")) as name_cfg_file:
57 return name_cfg_file.read().rstrip("\n").strip()
62 def theme_dirs_from_prefix(prefix):
63 if not os.path.exists(prefix):
66 for potential_theme in os.listdir(prefix):
67 theme_full_path = os.path.join(prefix, potential_theme)
68 theme_css = os.path.join(theme_full_path, "style.css")
69 if not os.path.exists(theme_css):
71 theme_name = load_theme_name(theme_full_path)
72 if theme_name is None:
74 theme_paths[theme_name] = prefix, potential_theme
77 def load_theme_paths():
79 prefixes = (util.local_data_dir(), util.appdata_dir())
80 for prefix in prefixes:
81 theme_paths.update(theme_dirs_from_prefix(prefix))
87 class TransactionWindow(QDialog):
90 label = unicode(self.label_edit.text())
91 self.parent.wallet.labels[self.tx_id] = label
93 super(TransactionWindow, self).accept()
95 def __init__(self, transaction_id, parent):
96 super(TransactionWindow, self).__init__()
98 self.tx_id = str(transaction_id)
103 self.setWindowTitle(_("Transaction successfully sent"))
105 self.layout = QGridLayout(self)
106 history_label = "%s\n%s" % (_("Your transaction has been sent."), _("Please enter a label for this transaction for future reference."))
107 self.layout.addWidget(QLabel(history_label))
109 self.label_edit = QLineEdit()
110 self.label_edit.setPlaceholderText(_("Transaction label"))
111 self.label_edit.setObjectName("label_input")
112 self.label_edit.setAttribute(Qt.WA_MacShowFocusRect, 0)
113 self.label_edit.setFocusPolicy(Qt.ClickFocus)
114 self.layout.addWidget(self.label_edit)
116 self.save_button = QPushButton(_("Save"))
117 self.layout.addWidget(self.save_button)
118 self.save_button.clicked.connect(self.set_label)
122 class MiniWindow(QDialog):
124 def __init__(self, actuator, expand_callback, config):
125 super(MiniWindow, self).__init__()
127 self.actuator = actuator
129 self.btc_balance = None
130 self.use_exchanges = ["Blockchain", "CoinDesk"]
131 self.quote_currencies = ["BRL", "CNY", "EUR", "GBP", "RUB", "USD"]
132 self.actuator.set_configured_currency(self.set_quote_currency)
133 self.actuator.set_configured_exchange(self.set_exchange)
135 # Needed because price discovery is done in a different thread
136 # which needs to be sent back to this main one to update the GUI
137 self.connect(self, SIGNAL("refresh_balance()"), self.refresh_balance)
139 self.balance_label = BalanceLabel(self.change_quote_currency, self)
140 self.balance_label.setObjectName("balance_label")
143 # Bitcoin address code
144 self.address_input = QLineEdit()
145 self.address_input.setPlaceholderText(_("Enter a Bitcoin address or contact"))
146 self.address_input.setObjectName("address_input")
148 self.address_input.setFocusPolicy(Qt.ClickFocus)
150 self.address_input.textChanged.connect(self.address_field_changed)
151 resize_line_edit_width(self.address_input,
152 "1BtaFUr3qVvAmwrsuDuu5zk6e4s2rxd2Gy")
154 self.address_completions = QStringListModel()
155 address_completer = QCompleter(self.address_input)
156 address_completer.setCaseSensitivity(False)
157 address_completer.setModel(self.address_completions)
158 self.address_input.setCompleter(address_completer)
160 address_layout = QHBoxLayout()
161 address_layout.addWidget(self.address_input)
163 self.amount_input = QLineEdit()
164 self.amount_input.setPlaceholderText(_("... and amount") + " (%s)"%self.actuator.g.base_unit())
165 self.amount_input.setObjectName("amount_input")
167 self.amount_input.setFocusPolicy(Qt.ClickFocus)
168 # This is changed according to the user's displayed balance
169 self.amount_validator = QDoubleValidator(self.amount_input)
170 self.amount_validator.setNotation(QDoubleValidator.StandardNotation)
171 self.amount_validator.setDecimals(8)
172 self.amount_input.setValidator(self.amount_validator)
174 # This removes the very ugly OSX highlighting, please leave this in :D
175 self.address_input.setAttribute(Qt.WA_MacShowFocusRect, 0)
176 self.amount_input.setAttribute(Qt.WA_MacShowFocusRect, 0)
177 self.amount_input.textChanged.connect(self.amount_input_changed)
179 #if self.actuator.g.wallet.seed:
180 self.send_button = QPushButton(_("&Send"))
182 # self.send_button = QPushButton(_("&Create"))
184 self.send_button.setObjectName("send_button")
185 self.send_button.setDisabled(True);
186 self.send_button.clicked.connect(self.send)
188 # Creating the receive button
189 self.switch_button = QPushButton( QIcon(":icons/switchgui.png"),'' )
190 self.switch_button.setMaximumWidth(25)
191 self.switch_button.setFlat(True)
192 self.switch_button.clicked.connect(expand_callback)
194 main_layout = QGridLayout(self)
196 main_layout.addWidget(self.balance_label, 0, 0, 1, 3)
197 main_layout.addWidget(self.switch_button, 0, 3)
199 main_layout.addWidget(self.address_input, 1, 0, 1, 4)
200 main_layout.addWidget(self.amount_input, 2, 0, 1, 2)
201 main_layout.addWidget(self.send_button, 2, 2, 1, 2)
203 self.send_button.setMaximumWidth(125)
205 self.history_list = history_widget.HistoryWidget()
206 self.history_list.setObjectName("history")
207 self.history_list.hide()
208 self.history_list.setAlternatingRowColors(True)
210 main_layout.addWidget(self.history_list, 3, 0, 1, 4)
212 self.receiving = receiving_widget.ReceivingWidget(self)
213 self.receiving.setObjectName("receiving")
215 # Add to the right side
216 self.receiving_box = QGroupBox(_("Select a receiving address"))
217 extra_layout = QGridLayout()
219 # Checkbox to filter used addresses
220 hide_used = QCheckBox(_('Hide used addresses'))
221 hide_used.setChecked(True)
222 hide_used.stateChanged.connect(self.receiving.toggle_used)
224 # Events for receiving addresses
225 self.receiving.clicked.connect(self.receiving.copy_address)
226 self.receiving.itemDoubleClicked.connect(self.receiving.edit_label)
227 self.receiving.itemChanged.connect(self.receiving.update_label)
231 extra_layout.addWidget( QLabel(_('Selecting an address will copy it to the clipboard.') + '\n' + _('Double clicking the label will allow you to edit it.') ),0,0)
233 extra_layout.addWidget(self.receiving, 1,0)
234 extra_layout.addWidget(hide_used, 2,0)
235 extra_layout.setColumnMinimumWidth(0,200)
237 self.receiving_box.setLayout(extra_layout)
238 main_layout.addWidget(self.receiving_box,0,4,-1,3)
239 self.receiving_box.hide()
241 self.main_layout = main_layout
243 quit_shortcut = QShortcut(QKeySequence("Ctrl+Q"), self)
244 quit_shortcut.activated.connect(self.close)
245 close_shortcut = QShortcut(QKeySequence("Ctrl+W"), self)
246 close_shortcut.activated.connect(self.close)
248 g = self.config.get("winpos-lite",[4, 25, 351, 149])
249 self.setGeometry(g[0], g[1], g[2], g[3])
251 show_hist = self.config.get("gui_show_history",False)
252 self.show_history(show_hist)
253 show_hist = self.config.get("gui_show_receiving",False)
254 self.toggle_receiving_layout(show_hist)
256 self.setWindowIcon(QIcon(":icons/electrum.png"))
257 self.setWindowTitle("Electrum")
258 self.setWindowFlags(Qt.Window|Qt.MSWindowsFixedSizeDialogHint)
259 self.layout().setSizeConstraint(QLayout.SetFixedSize)
260 self.setObjectName("main_window")
263 def context_menu(self):
265 themes_menu = view_menu.addMenu(_("&Themes"))
266 selected_theme = self.actuator.selected_theme()
267 theme_group = QActionGroup(self)
268 for theme_name in self.actuator.theme_names():
269 theme_action = themes_menu.addAction(theme_name)
270 theme_action.setCheckable(True)
271 if selected_theme == theme_name:
272 theme_action.setChecked(True)
273 class SelectThemeFunctor:
274 def __init__(self, theme_name, toggle_theme):
275 self.theme_name = theme_name
276 self.toggle_theme = toggle_theme
277 def __call__(self, checked):
279 self.toggle_theme(self.theme_name)
280 delegate = SelectThemeFunctor(theme_name, self.toggle_theme)
281 theme_action.toggled.connect(delegate)
282 theme_group.addAction(theme_action)
283 view_menu.addSeparator()
285 show_receiving = view_menu.addAction(_("Show Receiving addresses"))
286 show_receiving.setCheckable(True)
287 show_receiving.toggled.connect(self.toggle_receiving_layout)
288 show_receiving.setChecked(self.config.get("gui_show_receiving",False))
290 show_history = view_menu.addAction(_("Show History"))
291 show_history.setCheckable(True)
292 show_history.toggled.connect(self.show_history)
293 show_history.setChecked(self.config.get("gui_show_history",False))
299 def toggle_theme(self, theme_name):
300 self.actuator.change_theme(theme_name)
301 # Recompute style globally
302 qApp.style().unpolish(self)
303 qApp.style().polish(self)
305 def closeEvent(self, event):
307 self.config.set_key("winpos-lite", [g.left(),g.top(),g.width(),g.height()],True)
308 self.actuator.g.closeEvent(event)
311 def pay_from_URI(self, URI):
313 dest_address, amount, label, message, request_url = util.parse_URI(URI)
316 self.address_input.setText(dest_address)
317 self.address_field_changed(dest_address)
318 self.amount_input.setText(str(amount))
323 def deactivate(self):
326 def set_exchange(self, use_exchange):
327 if use_exchange not in self.use_exchanges:
329 self.use_exchanges.remove(use_exchange)
330 self.use_exchanges.insert(0, use_exchange)
331 self.refresh_balance()
333 def set_quote_currency(self, currency):
334 """Set and display the fiat currency country."""
335 if currency not in self.quote_currencies:
337 self.quote_currencies.remove(currency)
338 self.quote_currencies.insert(0, currency)
339 self.refresh_balance()
341 def change_quote_currency(self, forward=True):
343 self.quote_currencies = \
344 self.quote_currencies[1:] + self.quote_currencies[0:1]
346 self.quote_currencies = \
347 self.quote_currencies[-1:] + self.quote_currencies[0:-1]
348 self.actuator.set_config_currency(self.quote_currencies[0])
349 self.refresh_balance()
351 def refresh_balance(self):
352 if self.btc_balance is None:
353 # Price has been discovered before wallet has been loaded
354 # and server connect... so bail.
356 self.set_balances(self.btc_balance)
357 self.amount_input_changed(self.amount_input.text())
359 def set_balances(self, btc_balance):
360 """Set the bitcoin balance and update the amount label accordingly."""
361 self.btc_balance = btc_balance
362 quote_text = self.create_quote_text(btc_balance)
364 quote_text = "(%s)" % quote_text
366 amount = self.actuator.g.format_amount(btc_balance)
367 unit = self.actuator.g.base_unit()
369 self.balance_label.set_balance_text(amount, unit, quote_text)
370 self.setWindowTitle("Electrum %s - %s %s" % (electrum_version, amount, unit))
372 def amount_input_changed(self, amount_text):
373 """Update the number of bitcoins displayed."""
374 self.check_button_status()
377 amount = D(str(amount_text)) * (10**self.actuator.g.decimal_point)
378 except decimal.InvalidOperation:
379 self.balance_label.show_balance()
381 quote_text = self.create_quote_text(amount)
383 self.balance_label.set_amount_text(quote_text)
384 self.balance_label.show_amount()
386 self.balance_label.show_balance()
388 def create_quote_text(self, btc_balance):
389 """Return a string copy of the amount fiat currency the
390 user has in bitcoins."""
391 from electrum.plugins import run_hook
393 run_hook('get_fiat_balance_text', btc_balance, r)
397 if self.actuator.send(self.address_input.text(),
398 self.amount_input.text(), self):
399 self.address_input.setText("")
400 self.amount_input.setText("")
402 def check_button_status(self):
403 """Check that the bitcoin address is valid and that something
404 is entered in the amount before making the send button clickable."""
406 value = D(str(self.amount_input.text())) * (10**self.actuator.g.decimal_point)
407 except decimal.InvalidOperation:
409 # self.address_input.property(...) returns a qVariant, not a bool.
410 # The == is needed to properly invoke a comparison.
411 if (self.address_input.property("isValid") == True and
412 value is not None and 0 < value <= self.btc_balance):
413 self.send_button.setDisabled(False)
415 self.send_button.setDisabled(True)
417 def address_field_changed(self, address):
418 # label or alias, with address in brackets
419 match2 = re.match("(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>",
422 address = match2.group(2)
423 self.address_input.setText(address)
425 if is_valid(address):
426 self.check_button_status()
427 self.address_input.setProperty("isValid", True)
428 self.recompute_style(self.address_input)
430 self.send_button.setDisabled(True)
431 self.address_input.setProperty("isValid", False)
432 self.recompute_style(self.address_input)
434 if len(address) == 0:
435 self.address_input.setProperty("isValid", None)
436 self.recompute_style(self.address_input)
438 def recompute_style(self, element):
439 self.style().unpolish(element)
440 self.style().polish(element)
442 def copy_address(self):
443 receive_popup = ReceivePopup(self.receive_button)
444 self.actuator.copy_address(receive_popup)
446 def update_completions(self, completions):
447 self.address_completions.setStringList(completions)
450 def update_history(self, tx_history):
452 self.history_list.empty()
454 for item in tx_history[-10:]:
455 tx_hash, conf, is_mine, value, fee, balance, timestamp = item
456 label = self.actuator.g.wallet.get_label(tx_hash)[0]
457 v_str = self.actuator.g.format_amount(value, True)
458 self.history_list.append(label, v_str, age(timestamp))
461 def the_website(self):
462 webbrowser.open("http://electrum.org")
465 def toggle_receiving_layout(self, toggle_state):
467 self.receiving_box.show()
469 self.receiving_box.hide()
470 self.config.set_key("gui_show_receiving", toggle_state)
472 def show_history(self, toggle_state):
474 self.main_layout.setRowMinimumHeight(3,200)
475 self.history_list.show()
477 self.main_layout.setRowMinimumHeight(3,0)
478 self.history_list.hide()
479 self.config.set_key("gui_show_history", toggle_state)
481 class BalanceLabel(QLabel):
487 def __init__(self, change_quote_currency, parent=None):
488 super(QLabel, self).__init__(_("Connecting..."), parent)
489 self.change_quote_currency = change_quote_currency
490 self.state = self.SHOW_CONNECTING
491 self.balance_text = ""
492 self.amount_text = ""
495 def mousePressEvent(self, event):
496 """Change the fiat currency selection if window background is clicked."""
497 if self.state != self.SHOW_CONNECTING:
498 if event.button() == Qt.LeftButton:
499 self.change_quote_currency()
501 position = event.globalPos()
502 menu = self.parent.context_menu()
506 def set_balance_text(self, amount, unit, quote_text):
507 """Set the amount of bitcoins in the gui."""
508 if self.state == self.SHOW_CONNECTING:
509 self.state = self.SHOW_BALANCE
511 self.balance_text = "<span style='font-size: 18pt'>%s</span>"%amount\
512 + " <span style='font-size: 10pt'>%s</span>" % unit \
513 + " <span style='font-size: 10pt'>%s</span>" % quote_text
515 if self.state == self.SHOW_BALANCE:
516 self.setText(self.balance_text)
518 def set_amount_text(self, quote_text):
519 self.amount_text = "<span style='font-size: 10pt'>%s</span>" % quote_text
520 if self.state == self.SHOW_AMOUNT:
521 self.setText(self.amount_text)
523 def show_balance(self):
524 if self.state == self.SHOW_AMOUNT:
525 self.state = self.SHOW_BALANCE
526 self.setText(self.balance_text)
528 def show_amount(self):
529 if self.state == self.SHOW_BALANCE:
530 self.state = self.SHOW_AMOUNT
531 self.setText(self.amount_text)
533 def ok_cancel_buttons(dialog):
534 row_layout = QHBoxLayout()
535 row_layout.addStretch(1)
536 ok_button = QPushButton(_("OK"))
537 row_layout.addWidget(ok_button)
538 ok_button.clicked.connect(dialog.accept)
539 cancel_button = QPushButton(_("Cancel"))
540 row_layout.addWidget(cancel_button)
541 cancel_button.clicked.connect(dialog.reject)
544 class PasswordDialog(QDialog):
546 def __init__(self, parent):
547 super(QDialog, self).__init__(parent)
551 self.password_input = QLineEdit()
552 self.password_input.setEchoMode(QLineEdit.Password)
554 main_layout = QVBoxLayout(self)
555 message = _('Please enter your password')
556 main_layout.addWidget(QLabel(message))
560 grid.addWidget(QLabel(_('Password')), 1, 0)
561 grid.addWidget(self.password_input, 1, 1)
562 main_layout.addLayout(grid)
564 main_layout.addLayout(ok_cancel_buttons(self))
565 self.setLayout(main_layout)
570 return unicode(self.password_input.text())
572 class ReceivePopup(QDialog):
574 def leaveEvent(self, event):
577 def setup(self, address):
578 label = QLabel(_("Copied your Bitcoin address to the clipboard!"))
579 address_display = QLineEdit(address)
580 address_display.setReadOnly(True)
581 resize_line_edit_width(address_display, address)
583 main_layout = QVBoxLayout(self)
584 main_layout.addWidget(label)
585 main_layout.addWidget(address_display)
587 self.setMouseTracking(True)
588 self.setWindowTitle("Electrum - " + _("Receive Bitcoin payment"))
589 self.setWindowFlags(Qt.Window|Qt.FramelessWindowHint|
590 Qt.MSWindowsFixedSizeDialogHint)
591 self.layout().setSizeConstraint(QLayout.SetFixedSize)
592 #self.setFrameStyle(QFrame.WinPanel|QFrame.Raised)
593 #self.setAlignment(Qt.AlignCenter)
596 parent = self.parent()
597 top_left_pos = parent.mapToGlobal(parent.rect().bottomLeft())
598 self.move(top_left_pos)
599 center_mouse_pos = self.mapToGlobal(self.rect().center())
600 QCursor.setPos(center_mouse_pos)
604 """Initialize the definitions relating to themes and
605 sending/receiving bitcoins."""
608 def __init__(self, main_window):
609 """Retrieve the gui theme used in previous session."""
611 self.theme_name = self.g.config.get('litegui_theme','Cleanlook')
612 self.themes = load_theme_paths()
615 def load_theme(self):
616 """Load theme retrieved from wallet file."""
618 theme_prefix, theme_path = self.themes[self.theme_name]
620 util.print_error("Theme not found!", self.theme_name)
622 full_theme_path = "%s/%s/style.css" % (theme_prefix, theme_path)
623 with open(full_theme_path) as style_file:
624 qApp.setStyleSheet(style_file.read())
626 def theme_names(self):
628 return sorted(self.themes.keys())
630 def selected_theme(self):
632 return self.theme_name
634 def change_theme(self, theme_name):
636 self.theme_name = theme_name
637 self.g.config.set_key('litegui_theme',theme_name)
640 def set_configured_exchange(self, set_exchange):
641 use_exchange = self.g.config.get('use_exchange')
642 if use_exchange is not None:
643 set_exchange(use_exchange)
645 def set_configured_currency(self, set_quote_currency):
646 """Set the inital fiat currency conversion country (USD/EUR/GBP) in
647 the GUI to what it was set to in the wallet."""
648 currency = self.g.config.get('currency')
649 # currency can be none when Electrum is used for the first
650 # time and no setting has been created yet.
651 if currency is not None:
652 set_quote_currency(currency)
654 def set_config_exchange(self, conversion_exchange):
655 self.g.config.set_key('exchange',conversion_exchange,True)
656 self.g.update_status()
658 def set_config_currency(self, conversion_currency):
659 """Change the wallet fiat currency country."""
660 self.g.config.set_key('currency',conversion_currency,True)
661 self.g.update_status()
663 def copy_address(self, receive_popup):
664 """Copy the wallet addresses into the client."""
665 addrs = [addr for addr in self.g.wallet.addresses(True)
666 if not self.g.wallet.is_change(addr)]
667 # Select most recent addresses from gap limit
668 addrs = addrs[-self.g.wallet.gap_limit:]
669 copied_address = random.choice(addrs)
670 qApp.clipboard().setText(copied_address)
671 receive_popup.setup(copied_address)
672 receive_popup.popup()
674 def waiting_dialog(self, f):
679 w.setWindowTitle('Electrum')
680 l = QLabel(_('Sending transaction, please wait.'))
689 w.connect(s, QtCore.SIGNAL('timersignal'), ff)
694 def send(self, address, amount, parent_window):
695 """Send bitcoins to the target address."""
696 dest_address = self.fetch_destination(address)
698 if dest_address is None or not is_valid(dest_address):
699 QMessageBox.warning(parent_window, _('Error'),
700 _('Invalid Bitcoin Address') + ':\n' + address, _('OK'))
703 amount = D(unicode(amount)) * (10*self.g.decimal_point)
704 print "amount", amount
707 if self.g.wallet.use_encryption:
708 password_dialog = PasswordDialog(parent_window)
709 password = password_dialog.run()
717 if amount < bitcoin(1) / 10:
719 fee = bitcoin(1) / 1000
722 tx = self.g.wallet.mktx([(dest_address, amount)], password, fee)
723 except Exception as error:
724 QMessageBox.warning(parent_window, _('Error'), str(error), _('OK'))
728 h = self.g.wallet.send_tx(tx)
730 self.waiting_dialog(lambda: False if self.g.wallet.tx_event.isSet() else _("Sending transaction, please wait..."))
732 status, message = self.g.wallet.receive_tx(h, tx)
736 dumpf = tempfile.NamedTemporaryFile(delete=False)
739 print "Dumped error tx to", dumpf.name
740 QMessageBox.warning(parent_window, _('Error'), message, _('OK'))
743 TransactionWindow(message, self)
745 filename = 'unsigned_tx_%s' % (time.mktime(time.gmtime()))
747 fileName = QFileDialog.getSaveFileName(QWidget(), _("Select a transaction filename"), os.path.expanduser('~/%s' % (filename)))
748 with open(fileName,'w') as f:
749 f.write(json.dumps(tx.as_dict(),indent=4) + '\n')
750 QMessageBox.information(QWidget(), _('Unsigned transaction created'), _("Unsigned transaction was saved to file:") + " " +fileName, _('OK'))
751 except Exception as e:
752 QMessageBox.warning(QWidget(), _('Error'), _('Could not write transaction to file: %s' % e), _('OK'))
755 def fetch_destination(self, address):
756 recipient = unicode(address).strip()
759 match1 = re.match("^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$",
762 # label or alias, with address in brackets
763 match2 = re.match("(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>",
768 self.g.wallet.get_alias(recipient, True,
769 self.show_message, self.question)
772 return match2.group(2)
780 class MiniDriver(QObject):
787 def __init__(self, main_window, mini_window):
788 super(QObject, self).__init__()
791 self.network = main_window.network
792 self.window = mini_window
795 self.network.register_callback('updated',self.update_callback)
796 self.network.register_callback('connected', self.update_callback)
797 self.network.register_callback('disconnected', self.update_callback)
802 self.connect(self, SIGNAL("updatesignal()"), self.update)
803 self.update_callback()
805 # This is a hack to workaround that Qt does not like changing the
806 # window properties from this other thread before the runloop has
808 def update_callback(self):
809 self.emit(SIGNAL("updatesignal()"))
814 elif not self.network.interface:
816 elif not self.network.interface.is_connected:
819 if self.g.wallet is None:
821 elif not self.g.wallet.up_to_date:
825 self.update_balance()
826 self.update_completions()
827 self.update_history()
828 self.window.receiving.update_list()
831 def initializing(self):
832 if self.state == self.INITIALIZING:
834 self.state = self.INITIALIZING
835 self.window.deactivate()
837 def connecting(self):
838 if self.state == self.CONNECTING:
840 self.state = self.CONNECTING
841 self.window.deactivate()
843 def synchronizing(self):
844 if self.state == self.SYNCHRONIZING:
846 self.state = self.SYNCHRONIZING
847 self.window.deactivate()
850 if self.state == self.READY:
852 self.state = self.READY
853 self.window.activate()
855 def update_balance(self):
856 conf_balance, unconf_balance = self.g.wallet.get_balance()
857 balance = D(conf_balance + unconf_balance)
858 self.window.set_balances(balance)
860 def update_completions(self):
862 for addr, label in self.g.wallet.labels.items():
863 if addr in self.g.wallet.addressbook:
864 completions.append("%s <%s>" % (label, addr))
865 self.window.update_completions(completions)
867 def update_history(self):
868 tx_history = self.g.wallet.get_tx_history()
869 self.window.update_history(tx_history)
872 if __name__ == "__main__":
873 app = QApplication(sys.argv)
874 with open(rsrc("style.css")) as style_file:
875 app.setStyleSheet(style_file.read())
877 sys.exit(app.exec_())