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
33 from electrum.version import ELECTRUM_VERSION as electrum_version
34 from electrum.util import format_satoshis, age
36 from main_window import ElectrumWindow
41 bitcoin = lambda v: v * 100000000
43 def IconButton(filename, parent=None):
44 pixmap = QPixmap(filename)
46 return QPushButton(icon, "", parent)
51 self.emit(SIGNAL('timersignal'))
54 def resize_line_edit_width(line_edit, text_input):
55 metrics = QFontMetrics(qApp.font())
56 # Create an extra character to add some space on the end
58 line_edit.setMinimumWidth(metrics.width(text_input))
60 def load_theme_name(theme_path):
62 with open(os.path.join(theme_path, "name.cfg")) as name_cfg_file:
63 return name_cfg_file.read().rstrip("\n").strip()
68 def theme_dirs_from_prefix(prefix):
69 if not os.path.exists(prefix):
72 for potential_theme in os.listdir(prefix):
73 theme_full_path = os.path.join(prefix, potential_theme)
74 theme_css = os.path.join(theme_full_path, "style.css")
75 if not os.path.exists(theme_css):
77 theme_name = load_theme_name(theme_full_path)
78 if theme_name is None:
80 theme_paths[theme_name] = prefix, potential_theme
83 def load_theme_paths():
85 prefixes = (util.local_data_dir(), util.appdata_dir())
86 for prefix in prefixes:
87 theme_paths.update(theme_dirs_from_prefix(prefix))
91 def csv_transaction(wallet):
93 select_export = _('Select file to export your wallet transactions to')
94 fileName = QFileDialog.getSaveFileName(QWidget(), select_export, os.path.expanduser('~/electrum-history.csv'), "*.csv")
96 with open(fileName, "w+") as csvfile:
97 transaction = csv.writer(csvfile)
98 transaction.writerow(["transaction_hash","label", "confirmations", "value", "fee", "balance", "timestamp"])
99 for item in wallet.get_tx_history():
100 tx_hash, confirmations, is_mine, value, fee, balance, timestamp = item
102 if timestamp is not None:
104 time_string = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
105 except [RuntimeError, TypeError, NameError] as reason:
106 time_string = "unknown"
109 time_string = "unknown"
111 time_string = "pending"
113 if value is not None:
114 value_string = format_satoshis(value, True)
119 fee_string = format_satoshis(fee, True)
124 label, is_default_label = wallet.get_label(tx_hash)
128 balance_string = format_satoshis(balance, False)
129 transaction.writerow([tx_hash, label, confirmations, value_string, fee_string, balance_string, time_string])
130 QMessageBox.information(None,"CSV Export created", "Your CSV export has been successfully created.")
131 except (IOError, os.error), reason:
132 export_error_label = _("Electrum was unable to produce a transaction export.")
133 QMessageBox.critical(None,_("Unable to create csv"), export_error_label + "\n" + str(reason))
137 class TransactionWindow(QDialog):
140 label = unicode(self.label_edit.text())
141 self.parent.wallet.labels[self.tx_id] = label
143 super(TransactionWindow, self).accept()
145 def __init__(self, transaction_id, parent):
146 super(TransactionWindow, self).__init__()
148 self.tx_id = str(transaction_id)
153 self.setWindowTitle(_("Transaction successfully sent"))
155 self.layout = QGridLayout(self)
156 history_label = "%s\n%s" % (_("Your transaction has been sent."), _("Please enter a label for this transaction for future reference."))
157 self.layout.addWidget(QLabel(history_label))
159 self.label_edit = QLineEdit()
160 self.label_edit.setPlaceholderText(_("Transaction label"))
161 self.label_edit.setObjectName("label_input")
162 self.label_edit.setAttribute(Qt.WA_MacShowFocusRect, 0)
163 self.label_edit.setFocusPolicy(Qt.ClickFocus)
164 self.layout.addWidget(self.label_edit)
166 self.save_button = QPushButton(_("Save"))
167 self.layout.addWidget(self.save_button)
168 self.save_button.clicked.connect(self.set_label)
172 class MiniWindow(QDialog):
174 def __init__(self, actuator, expand_callback, config):
175 super(MiniWindow, self).__init__()
177 self.actuator = actuator
179 self.btc_balance = None
180 self.quote_currencies = ["BRL", "CNY", "EUR", "GBP", "RUB", "USD"]
181 self.actuator.set_configured_currency(self.set_quote_currency)
183 # Needed because price discovery is done in a different thread
184 # which needs to be sent back to this main one to update the GUI
185 self.connect(self, SIGNAL("refresh_balance()"), self.refresh_balance)
187 self.balance_label = BalanceLabel(self.change_quote_currency, self)
188 self.balance_label.setObjectName("balance_label")
191 # Bitcoin address code
192 self.address_input = QLineEdit()
193 self.address_input.setPlaceholderText(_("Enter a Bitcoin address or contact"))
194 self.address_input.setObjectName("address_input")
196 self.address_input.setFocusPolicy(Qt.ClickFocus)
198 self.address_input.textChanged.connect(self.address_field_changed)
199 resize_line_edit_width(self.address_input,
200 "1BtaFUr3qVvAmwrsuDuu5zk6e4s2rxd2Gy")
202 self.address_completions = QStringListModel()
203 address_completer = QCompleter(self.address_input)
204 address_completer.setCaseSensitivity(False)
205 address_completer.setModel(self.address_completions)
206 self.address_input.setCompleter(address_completer)
208 address_layout = QHBoxLayout()
209 address_layout.addWidget(self.address_input)
211 self.amount_input = QLineEdit()
212 self.amount_input.setPlaceholderText(_("... and amount") + " (%s)"%self.actuator.g.base_unit())
213 self.amount_input.setObjectName("amount_input")
215 self.amount_input.setFocusPolicy(Qt.ClickFocus)
216 # This is changed according to the user's displayed balance
217 self.amount_validator = QDoubleValidator(self.amount_input)
218 self.amount_validator.setNotation(QDoubleValidator.StandardNotation)
219 self.amount_validator.setDecimals(8)
220 self.amount_input.setValidator(self.amount_validator)
222 # This removes the very ugly OSX highlighting, please leave this in :D
223 self.address_input.setAttribute(Qt.WA_MacShowFocusRect, 0)
224 self.amount_input.setAttribute(Qt.WA_MacShowFocusRect, 0)
225 self.amount_input.textChanged.connect(self.amount_input_changed)
227 #if self.actuator.g.wallet.seed:
228 self.send_button = QPushButton(_("&Send"))
230 # self.send_button = QPushButton(_("&Create"))
232 self.send_button.setObjectName("send_button")
233 self.send_button.setDisabled(True);
234 self.send_button.clicked.connect(self.send)
236 # Creating the receive button
237 self.switch_button = QPushButton( QIcon(":icons/switchgui.png"),'' )
238 self.switch_button.setMaximumWidth(25)
239 self.switch_button.setFlat(True)
240 self.switch_button.clicked.connect(expand_callback)
242 main_layout = QGridLayout(self)
244 main_layout.addWidget(self.balance_label, 0, 0, 1, 3)
245 main_layout.addWidget(self.switch_button, 0, 3)
247 main_layout.addWidget(self.address_input, 1, 0, 1, 4)
248 main_layout.addWidget(self.amount_input, 2, 0, 1, 2)
249 main_layout.addWidget(self.send_button, 2, 2, 1, 2)
251 self.send_button.setMaximumWidth(125)
253 self.history_list = history_widget.HistoryWidget()
254 self.history_list.setObjectName("history")
255 self.history_list.hide()
256 self.history_list.setAlternatingRowColors(True)
258 main_layout.addWidget(self.history_list, 3, 0, 1, 4)
260 self.receiving = receiving_widget.ReceivingWidget(self)
261 self.receiving.setObjectName("receiving")
263 # Add to the right side
264 self.receiving_box = QGroupBox(_("Select a receiving address"))
265 extra_layout = QGridLayout()
267 # Checkbox to filter used addresses
268 hide_used = QCheckBox(_('Hide used addresses'))
269 hide_used.setChecked(True)
270 hide_used.stateChanged.connect(self.receiving.toggle_used)
272 # Events for receiving addresses
273 self.receiving.clicked.connect(self.receiving.copy_address)
274 self.receiving.itemDoubleClicked.connect(self.receiving.edit_label)
275 self.receiving.itemChanged.connect(self.receiving.update_label)
279 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)
281 extra_layout.addWidget(self.receiving, 1,0)
282 extra_layout.addWidget(hide_used, 2,0)
283 extra_layout.setColumnMinimumWidth(0,200)
285 self.receiving_box.setLayout(extra_layout)
286 main_layout.addWidget(self.receiving_box,0,4,-1,3)
287 self.receiving_box.hide()
289 self.main_layout = main_layout
291 quit_shortcut = QShortcut(QKeySequence("Ctrl+Q"), self)
292 quit_shortcut.activated.connect(self.close)
293 close_shortcut = QShortcut(QKeySequence("Ctrl+W"), self)
294 close_shortcut.activated.connect(self.close)
296 g = self.config.get("winpos-lite",[4, 25, 351, 149])
297 self.setGeometry(g[0], g[1], g[2], g[3])
299 show_hist = self.config.get("gui_show_history",False)
300 self.show_history(show_hist)
301 show_hist = self.config.get("gui_show_receiving",False)
302 self.toggle_receiving_layout(show_hist)
304 self.setWindowIcon(QIcon(":icons/electrum.png"))
305 self.setWindowTitle("Electrum")
306 self.setWindowFlags(Qt.Window|Qt.MSWindowsFixedSizeDialogHint)
307 self.layout().setSizeConstraint(QLayout.SetFixedSize)
308 self.setObjectName("main_window")
311 def context_menu(self):
313 themes_menu = view_menu.addMenu(_("&Themes"))
314 selected_theme = self.actuator.selected_theme()
315 theme_group = QActionGroup(self)
316 for theme_name in self.actuator.theme_names():
317 theme_action = themes_menu.addAction(theme_name)
318 theme_action.setCheckable(True)
319 if selected_theme == theme_name:
320 theme_action.setChecked(True)
321 class SelectThemeFunctor:
322 def __init__(self, theme_name, toggle_theme):
323 self.theme_name = theme_name
324 self.toggle_theme = toggle_theme
325 def __call__(self, checked):
327 self.toggle_theme(self.theme_name)
328 delegate = SelectThemeFunctor(theme_name, self.toggle_theme)
329 theme_action.toggled.connect(delegate)
330 theme_group.addAction(theme_action)
331 view_menu.addSeparator()
333 show_receiving = view_menu.addAction(_("Show Receiving addresses"))
334 show_receiving.setCheckable(True)
335 show_receiving.toggled.connect(self.toggle_receiving_layout)
336 show_receiving.setChecked(self.config.get("gui_show_receiving",False))
338 show_history = view_menu.addAction(_("Show History"))
339 show_history.setCheckable(True)
340 show_history.toggled.connect(self.show_history)
341 show_history.setChecked(self.config.get("gui_show_history",False))
347 def toggle_theme(self, theme_name):
348 old_path = QDir.currentPath()
349 self.actuator.change_theme(theme_name)
350 # Recompute style globally
351 qApp.style().unpolish(self)
352 qApp.style().polish(self)
353 QDir.setCurrent(old_path)
355 def closeEvent(self, event):
357 self.config.set_key("winpos-lite", [g.left(),g.top(),g.width(),g.height()],True)
359 super(MiniWindow, self).closeEvent(event)
362 def set_payment_fields(self, dest_address, amount):
363 self.address_input.setText(dest_address)
364 self.address_field_changed(dest_address)
365 self.amount_input.setText(amount)
370 def deactivate(self):
373 def set_quote_currency(self, currency):
374 """Set and display the fiat currency country."""
375 if currency not in self.quote_currencies:
377 self.quote_currencies.remove(currency)
378 self.quote_currencies.insert(0, currency)
379 self.refresh_balance()
381 def change_quote_currency(self, forward=True):
383 self.quote_currencies = \
384 self.quote_currencies[1:] + self.quote_currencies[0:1]
386 self.quote_currencies = \
387 self.quote_currencies[-1:] + self.quote_currencies[0:-1]
388 self.actuator.set_config_currency(self.quote_currencies[0])
389 self.refresh_balance()
391 def refresh_balance(self):
392 if self.btc_balance is None:
393 # Price has been discovered before wallet has been loaded
394 # and server connect... so bail.
396 self.set_balances(self.btc_balance)
397 self.amount_input_changed(self.amount_input.text())
399 def set_balances(self, btc_balance):
400 """Set the bitcoin balance and update the amount label accordingly."""
401 self.btc_balance = btc_balance
402 quote_text = self.create_quote_text(btc_balance)
404 quote_text = "(%s)" % quote_text
406 amount = self.actuator.g.format_amount(btc_balance)
407 unit = self.actuator.g.base_unit()
409 self.balance_label.set_balance_text(amount, unit, quote_text)
410 self.setWindowTitle("Electrum %s - %s %s" % (electrum_version, amount, unit))
412 def amount_input_changed(self, amount_text):
413 """Update the number of bitcoins displayed."""
414 self.check_button_status()
417 amount = D(str(amount_text)) * (10**self.actuator.g.decimal_point)
418 except decimal.InvalidOperation:
419 self.balance_label.show_balance()
421 quote_text = self.create_quote_text(amount)
423 self.balance_label.set_amount_text(quote_text)
424 self.balance_label.show_amount()
426 self.balance_label.show_balance()
428 def create_quote_text(self, btc_balance):
429 """Return a string copy of the amount fiat currency the
430 user has in bitcoins."""
431 from electrum.plugins import run_hook
433 run_hook('set_quote_text', btc_balance, r)
437 if self.actuator.send(self.address_input.text(),
438 self.amount_input.text(), self):
439 self.address_input.setText("")
440 self.amount_input.setText("")
442 def check_button_status(self):
443 """Check that the bitcoin address is valid and that something
444 is entered in the amount before making the send button clickable."""
446 value = D(str(self.amount_input.text())) * (10**self.actuator.g.decimal_point)
447 except decimal.InvalidOperation:
449 # self.address_input.property(...) returns a qVariant, not a bool.
450 # The == is needed to properly invoke a comparison.
451 if (self.address_input.property("isValid") == True and
452 value is not None and 0 < value <= self.btc_balance):
453 self.send_button.setDisabled(False)
455 self.send_button.setDisabled(True)
457 def address_field_changed(self, address):
458 # label or alias, with address in brackets
459 match2 = re.match("(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>",
462 address = match2.group(2)
463 self.address_input.setText(address)
465 if is_valid(address):
466 self.check_button_status()
467 self.address_input.setProperty("isValid", True)
468 self.recompute_style(self.address_input)
470 self.send_button.setDisabled(True)
471 self.address_input.setProperty("isValid", False)
472 self.recompute_style(self.address_input)
474 if len(address) == 0:
475 self.address_input.setProperty("isValid", None)
476 self.recompute_style(self.address_input)
478 def recompute_style(self, element):
479 self.style().unpolish(element)
480 self.style().polish(element)
482 def copy_address(self):
483 receive_popup = ReceivePopup(self.receive_button)
484 self.actuator.copy_address(receive_popup)
486 def update_completions(self, completions):
487 self.address_completions.setStringList(completions)
490 def update_history(self, tx_history):
492 self.history_list.empty()
494 for item in tx_history[-10:]:
495 tx_hash, conf, is_mine, value, fee, balance, timestamp = item
496 label = self.actuator.g.wallet.get_label(tx_hash)[0]
497 v_str = self.actuator.g.format_amount(value, True)
498 self.history_list.append(label, v_str, age(timestamp))
501 def the_website(self):
502 webbrowser.open("http://electrum.org")
505 def toggle_receiving_layout(self, toggle_state):
507 self.receiving_box.show()
509 self.receiving_box.hide()
510 self.config.set_key("gui_show_receiving", toggle_state)
512 def show_history(self, toggle_state):
514 self.main_layout.setRowMinimumHeight(3,200)
515 self.history_list.show()
517 self.main_layout.setRowMinimumHeight(3,0)
518 self.history_list.hide()
519 self.config.set_key("gui_show_history", toggle_state)
521 class BalanceLabel(QLabel):
527 def __init__(self, change_quote_currency, parent=None):
528 super(QLabel, self).__init__(_("Connecting..."), parent)
529 self.change_quote_currency = change_quote_currency
530 self.state = self.SHOW_CONNECTING
531 self.balance_text = ""
532 self.amount_text = ""
535 def mousePressEvent(self, event):
536 """Change the fiat currency selection if window background is clicked."""
537 if self.state != self.SHOW_CONNECTING:
538 if event.button() == Qt.LeftButton:
539 self.change_quote_currency()
541 position = event.globalPos()
542 menu = self.parent.context_menu()
546 def set_balance_text(self, amount, unit, quote_text):
547 """Set the amount of bitcoins in the gui."""
548 if self.state == self.SHOW_CONNECTING:
549 self.state = self.SHOW_BALANCE
551 self.balance_text = "<span style='font-size: 18pt'>%s</span>"%amount\
552 + " <span style='font-size: 10pt'>%s</span>" % unit \
553 + " <span style='font-size: 10pt'>%s</span>" % quote_text
555 if self.state == self.SHOW_BALANCE:
556 self.setText(self.balance_text)
558 def set_amount_text(self, quote_text):
559 self.amount_text = "<span style='font-size: 10pt'>%s</span>" % quote_text
560 if self.state == self.SHOW_AMOUNT:
561 self.setText(self.amount_text)
563 def show_balance(self):
564 if self.state == self.SHOW_AMOUNT:
565 self.state = self.SHOW_BALANCE
566 self.setText(self.balance_text)
568 def show_amount(self):
569 if self.state == self.SHOW_BALANCE:
570 self.state = self.SHOW_AMOUNT
571 self.setText(self.amount_text)
573 def ok_cancel_buttons(dialog):
574 row_layout = QHBoxLayout()
575 row_layout.addStretch(1)
576 ok_button = QPushButton(_("OK"))
577 row_layout.addWidget(ok_button)
578 ok_button.clicked.connect(dialog.accept)
579 cancel_button = QPushButton(_("Cancel"))
580 row_layout.addWidget(cancel_button)
581 cancel_button.clicked.connect(dialog.reject)
584 class PasswordDialog(QDialog):
586 def __init__(self, parent):
587 super(QDialog, self).__init__(parent)
591 self.password_input = QLineEdit()
592 self.password_input.setEchoMode(QLineEdit.Password)
594 main_layout = QVBoxLayout(self)
595 message = _('Please enter your password')
596 main_layout.addWidget(QLabel(message))
600 grid.addWidget(QLabel(_('Password')), 1, 0)
601 grid.addWidget(self.password_input, 1, 1)
602 main_layout.addLayout(grid)
604 main_layout.addLayout(ok_cancel_buttons(self))
605 self.setLayout(main_layout)
610 return unicode(self.password_input.text())
612 class ReceivePopup(QDialog):
614 def leaveEvent(self, event):
617 def setup(self, address):
618 label = QLabel(_("Copied your Bitcoin address to the clipboard!"))
619 address_display = QLineEdit(address)
620 address_display.setReadOnly(True)
621 resize_line_edit_width(address_display, address)
623 main_layout = QVBoxLayout(self)
624 main_layout.addWidget(label)
625 main_layout.addWidget(address_display)
627 self.setMouseTracking(True)
628 self.setWindowTitle("Electrum - " + _("Receive Bitcoin payment"))
629 self.setWindowFlags(Qt.Window|Qt.FramelessWindowHint|
630 Qt.MSWindowsFixedSizeDialogHint)
631 self.layout().setSizeConstraint(QLayout.SetFixedSize)
632 #self.setFrameStyle(QFrame.WinPanel|QFrame.Raised)
633 #self.setAlignment(Qt.AlignCenter)
636 parent = self.parent()
637 top_left_pos = parent.mapToGlobal(parent.rect().bottomLeft())
638 self.move(top_left_pos)
639 center_mouse_pos = self.mapToGlobal(self.rect().center())
640 QCursor.setPos(center_mouse_pos)
644 """Initialize the definitions relating to themes and
645 sending/receiving bitcoins."""
648 def __init__(self, main_window):
649 """Retrieve the gui theme used in previous session."""
651 self.theme_name = self.g.config.get('litegui_theme','Cleanlook')
652 self.themes = load_theme_paths()
654 def load_theme(self):
655 """Load theme retrieved from wallet file."""
657 theme_prefix, theme_path = self.themes[self.theme_name]
659 util.print_error("Theme not found!", self.theme_name)
661 QDir.setCurrent(os.path.join(theme_prefix, theme_path))
662 with open(rsrc("style.css")) as style_file:
663 qApp.setStyleSheet(style_file.read())
665 def theme_names(self):
667 return sorted(self.themes.keys())
669 def selected_theme(self):
671 return self.theme_name
673 def change_theme(self, theme_name):
675 self.theme_name = theme_name
676 self.g.config.set_key('litegui_theme',theme_name)
679 def set_configured_currency(self, set_quote_currency):
680 """Set the inital fiat currency conversion country (USD/EUR/GBP) in
681 the GUI to what it was set to in the wallet."""
682 currency = self.g.config.get('currency')
683 # currency can be none when Electrum is used for the first
684 # time and no setting has been created yet.
685 if currency is not None:
686 set_quote_currency(currency)
688 def set_config_currency(self, conversion_currency):
689 """Change the wallet fiat currency country."""
690 self.g.config.set_key('currency',conversion_currency,True)
691 self.g.update_status()
693 def copy_address(self, receive_popup):
694 """Copy the wallet addresses into the client."""
695 addrs = [addr for addr in self.g.wallet.addresses(True)
696 if not self.g.wallet.is_change(addr)]
697 # Select most recent addresses from gap limit
698 addrs = addrs[-self.g.wallet.gap_limit:]
699 copied_address = random.choice(addrs)
700 qApp.clipboard().setText(copied_address)
701 receive_popup.setup(copied_address)
702 receive_popup.popup()
704 def waiting_dialog(self, f):
709 w.setWindowTitle('Electrum')
710 l = QLabel(_('Sending transaction, please wait.'))
719 w.connect(s, QtCore.SIGNAL('timersignal'), ff)
724 def send(self, address, amount, parent_window):
725 """Send bitcoins to the target address."""
726 dest_address = self.fetch_destination(address)
728 if dest_address is None or not is_valid(dest_address):
729 QMessageBox.warning(parent_window, _('Error'),
730 _('Invalid Bitcoin Address') + ':\n' + address, _('OK'))
733 amount = D(unicode(amount)) * (10*self.g.decimal_point)
734 print "amount", amount
737 if self.g.wallet.use_encryption:
738 password_dialog = PasswordDialog(parent_window)
739 password = password_dialog.run()
747 if amount < bitcoin(1) / 10:
749 fee = bitcoin(1) / 1000
752 tx = self.g.wallet.mktx([(dest_address, amount)], password, fee)
753 except BaseException as error:
754 QMessageBox.warning(parent_window, _('Error'), str(error), _('OK'))
758 h = self.g.wallet.send_tx(tx)
760 self.waiting_dialog(lambda: False if self.g.wallet.tx_event.isSet() else _("Sending transaction, please wait..."))
762 status, message = self.g.wallet.receive_tx(h)
766 dumpf = tempfile.NamedTemporaryFile(delete=False)
769 print "Dumped error tx to", dumpf.name
770 QMessageBox.warning(parent_window, _('Error'), message, _('OK'))
773 TransactionWindow(message, self)
775 filename = 'unsigned_tx_%s' % (time.mktime(time.gmtime()))
777 fileName = QFileDialog.getSaveFileName(QWidget(), _("Select a transaction filename"), os.path.expanduser('~/%s' % (filename)))
778 with open(fileName,'w') as f:
779 f.write(json.dumps(tx.as_dict(),indent=4) + '\n')
780 QMessageBox.information(QWidget(), _('Unsigned transaction created'), _("Unsigned transaction was saved to file:") + " " +fileName, _('OK'))
781 except BaseException as e:
782 QMessageBox.warning(QWidget(), _('Error'), _('Could not write transaction to file: %s' % e), _('OK'))
785 def fetch_destination(self, address):
786 recipient = unicode(address).strip()
789 match1 = re.match("^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$",
792 # label or alias, with address in brackets
793 match2 = re.match("(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>",
798 self.g.wallet.get_alias(recipient, True,
799 self.show_message, self.question)
802 return match2.group(2)
810 class MiniDriver(QObject):
817 def __init__(self, main_window, mini_window):
818 super(QObject, self).__init__()
821 self.network = main_window.network
822 self.window = mini_window
824 self.network.register_callback('updated',self.update_callback)
825 self.network.register_callback('connected', self.update_callback)
826 self.network.register_callback('disconnected', self.update_callback)
831 self.connect(self, SIGNAL("updatesignal()"), self.update)
832 self.update_callback()
834 # This is a hack to workaround that Qt does not like changing the
835 # window properties from this other thread before the runloop has
837 def update_callback(self):
838 self.emit(SIGNAL("updatesignal()"))
841 if not self.network.interface:
843 elif not self.network.interface.is_connected:
846 if self.g.wallet is None:
848 elif not self.g.wallet.up_to_date:
852 self.update_balance()
853 self.update_completions()
854 self.update_history()
855 self.window.receiving.update_list()
858 def initializing(self):
859 if self.state == self.INITIALIZING:
861 self.state = self.INITIALIZING
862 self.window.deactivate()
864 def connecting(self):
865 if self.state == self.CONNECTING:
867 self.state = self.CONNECTING
868 self.window.deactivate()
870 def synchronizing(self):
871 if self.state == self.SYNCHRONIZING:
873 self.state = self.SYNCHRONIZING
874 self.window.deactivate()
877 if self.state == self.READY:
879 self.state = self.READY
880 self.window.activate()
882 def update_balance(self):
883 conf_balance, unconf_balance = self.g.wallet.get_balance()
884 balance = D(conf_balance + unconf_balance)
885 self.window.set_balances(balance)
887 def update_completions(self):
889 for addr, label in self.g.wallet.labels.items():
890 if addr in self.g.wallet.addressbook:
891 completions.append("%s <%s>" % (label, addr))
892 self.window.update_completions(completions)
894 def update_history(self):
895 tx_history = self.g.wallet.get_tx_history()
896 self.window.update_history(tx_history)
899 if __name__ == "__main__":
900 app = QApplication(sys.argv)
901 with open(rsrc("style.css")) as style_file:
902 app.setStyleSheet(style_file.read())
904 sys.exit(app.exec_())