-from PyQt4.QtCore import *
-from PyQt4.QtGui import *
+import sys
+
+# Let's do some dep checking and handle missing ones gracefully
+try:
+ from PyQt4.QtCore import *
+ from PyQt4.QtGui import *
+ import PyQt4.QtCore as QtCore
+
+except ImportError:
+ print "You need to have PyQT installed to run Electrum in graphical mode."
+ print "If you have pip installed try 'sudo pip install pyqt' if you are on Debian/Ubuntu try 'sudo apt-get install python-qt4'."
+ sys.exit(0)
+
+
+
+
from decimal import Decimal as D
-from util import appdata_dir, get_resource_path as rsrc
+from util import get_resource_path as rsrc
from i18n import _
import decimal
import exchange_rate
import os.path
import random
import re
-import sys
import time
import wallet
import webbrowser
import history_widget
+import receiving_widget
+import util
+import csv
+import datetime
-try:
- import lib.gui_qt as gui_qt
-except ImportError:
- import electrum.gui_qt as gui_qt
+from version import ELECTRUM_VERSION as electrum_version
+from wallet import format_satoshis
+import gui_qt
+import shutil
bitcoin = lambda v: v * 100000000
text_input += "A"
line_edit.setMinimumWidth(metrics.width(text_input))
-def cd_data_dir():
- assert sys.argv
- prefix_path = os.path.dirname(sys.argv[0])
- local_data = os.path.join(prefix_path, "data")
- if os.path.exists(os.path.join(local_data, "style.css")):
- data_dir = local_data
- else:
- data_dir = appdata_dir()
- QDir.setCurrent(data_dir)
-
-class ElectrumGui:
+def load_theme_name(theme_path):
+ try:
+ with open(os.path.join(theme_path, "name.cfg")) as name_cfg_file:
+ return name_cfg_file.read().rstrip("\n").strip()
+ except IOError:
+ return None
+
+
+def theme_dirs_from_prefix(prefix):
+ if not os.path.exists(prefix):
+ return []
+ theme_paths = {}
+ for potential_theme in os.listdir(prefix):
+ theme_full_path = os.path.join(prefix, potential_theme)
+ theme_css = os.path.join(theme_full_path, "style.css")
+ if not os.path.exists(theme_css):
+ continue
+ theme_name = load_theme_name(theme_full_path)
+ if theme_name is None:
+ continue
+ theme_paths[theme_name] = prefix, potential_theme
+ return theme_paths
+
+def load_theme_paths():
+ theme_paths = {}
+ prefixes = (util.local_data_dir(), util.appdata_dir())
+ for prefix in prefixes:
+ theme_paths.update(theme_dirs_from_prefix(prefix))
+ return theme_paths
+
+
+class ElectrumGui(QObject):
+
+ def __init__(self, wallet, config, expert=None):
+ super(QObject, self).__init__()
- def __init__(self, wallet):
self.wallet = wallet
- self.app = QApplication(sys.argv)
- # Should probably not modify the current path but instead
- # change the behaviour of rsrc(...)
- self.old_path = QDir.currentPath()
- cd_data_dir()
- with open(rsrc("style.css")) as style_file:
- self.app.setStyleSheet(style_file.read())
+ self.config = config
+ self.check_qt_version()
+ self.expert = expert
+ if self.expert != None:
+ self.app = self.expert.app
+ else:
+ self.app = QApplication(sys.argv)
+
+ def check_qt_version(self):
+ qtVersion = qVersion()
+ if not(int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7):
+ app = QApplication(sys.argv)
+ 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")
+ self.config.set_key('gui','classic',True)
+ sys.exit(0)
+
def main(self, url):
actuator = MiniActuator(self.wallet)
- self.mini = MiniWindow(actuator, self.expand)
+ # Should probably not modify the current path but instead
+ # change the behaviour of rsrc(...)
+ old_path = QDir.currentPath()
+ actuator.load_theme()
+
+ self.mini = MiniWindow(actuator, self.expand, self.config)
driver = MiniDriver(self.wallet, self.mini)
# Reset path back to original value now that loading the GUI
# is completed.
- QDir.setCurrent(self.old_path)
+ QDir.setCurrent(old_path)
if url:
self.set_url(url)
-
- timer = Timer()
- timer.start()
- self.expert = gui_qt.ElectrumWindow(self.wallet)
- self.expert.app = self.app
- self.expert.connect_slots(timer)
- self.expert.update_wallet()
-
- self.app.exec_()
+
+ if self.expert == None:
+ timer = Timer()
+ timer.start()
+ self.expert = gui_qt.ElectrumWindow(self.wallet, self.config)
+ self.expert.app = self.app
+ self.expert.connect_slots(timer)
+ self.expert.update_wallet()
+ self.app.exec_()
def expand(self):
+ """Hide the lite mode window and show pro-mode."""
self.mini.hide()
self.expert.show()
qt_gui_object = gui_qt.ElectrumGui(self.wallet, self.app)
return qt_gui_object.restore_or_create()
+class TransactionWindow(QDialog):
+
+ def set_label(self):
+ label = unicode(self.label_edit.text())
+ self.parent.wallet.labels[self.tx_id] = label
+
+ super(TransactionWindow, self).accept()
+
+ def __init__(self, transaction_id, parent):
+ super(TransactionWindow, self).__init__()
+
+ self.tx_id = str(transaction_id)
+ self.parent = parent
+
+ self.setModal(True)
+ self.resize(200,100)
+ self.setWindowTitle("Transaction successfully sent")
+
+ self.layout = QGridLayout(self)
+ self.layout.addWidget(QLabel("Your transaction has been sent.\nPlease enter a label for this transaction for future reference."))
+
+ self.label_edit = QLineEdit()
+ self.label_edit.setPlaceholderText(_("Transaction label"))
+ self.label_edit.setObjectName("label_input")
+ self.label_edit.setAttribute(Qt.WA_MacShowFocusRect, 0)
+ self.label_edit.setFocusPolicy(Qt.ClickFocus)
+ self.layout.addWidget(self.label_edit)
+
+ self.save_button = QPushButton(_("Save"))
+ self.layout.addWidget(self.save_button)
+ self.save_button.clicked.connect(self.set_label)
+
+ self.exec_()
+
class MiniWindow(QDialog):
- def __init__(self, actuator, expand_callback):
+ def __init__(self, actuator, expand_callback, config):
super(MiniWindow, self).__init__()
+ tx = "e08115d0f7819aee65b9d24f81ef9d46eb62bb67ddef5318156cbc3ceb7b703e"
self.actuator = actuator
-
+ self.config = config
self.btc_balance = None
- self.quote_currencies = ["EUR", "USD", "GBP"]
+ self.quote_currencies = ["BRL", "CNY", "EUR", "GBP", "RUB", "USD"]
self.actuator.set_configured_currency(self.set_quote_currency)
self.exchanger = exchange_rate.Exchanger(self)
# Needed because price discovery is done in a different thread
self.balance_label = BalanceLabel(self.change_quote_currency)
self.balance_label.setObjectName("balance_label")
- self.receive_button = QPushButton(_("&Receive"))
- self.receive_button.setObjectName("receive_button")
- self.receive_button.setDefault(True)
- self.connect(self.receive_button, SIGNAL("clicked()"), self.copy_address)
# Bitcoin address code
self.address_input = QLineEdit()
- self.address_input.setPlaceholderText(_("Enter a Bitcoin address..."))
+ self.address_input.setPlaceholderText(_("Enter a Bitcoin address or contact"))
self.address_input.setObjectName("address_input")
+ self.address_input.setFocusPolicy(Qt.ClickFocus)
- self.connect(self.address_input, SIGNAL("textEdited(QString)"), self.address_field_changed)
- resize_line_edit_width(self.address_input, "1BtaFUr3qVvAmwrsuDuu5zk6e4s2rxd2Gy")
+ self.address_input.textChanged.connect(self.address_field_changed)
+ resize_line_edit_width(self.address_input,
+ "1BtaFUr3qVvAmwrsuDuu5zk6e4s2rxd2Gy")
self.address_completions = QStringListModel()
address_completer = QCompleter(self.address_input)
self.amount_input = QLineEdit()
self.amount_input.setPlaceholderText(_("... and amount"))
self.amount_input.setObjectName("amount_input")
+
+ self.amount_input.setFocusPolicy(Qt.ClickFocus)
# This is changed according to the user's displayed balance
self.amount_validator = QDoubleValidator(self.amount_input)
self.amount_validator.setNotation(QDoubleValidator.StandardNotation)
# This removes the very ugly OSX highlighting, please leave this in :D
self.address_input.setAttribute(Qt.WA_MacShowFocusRect, 0)
self.amount_input.setAttribute(Qt.WA_MacShowFocusRect, 0)
-
- self.connect(self.amount_input, SIGNAL("textChanged(QString)"),
- self.amount_input_changed)
+ self.amount_input.textChanged.connect(self.amount_input_changed)
self.send_button = QPushButton(_("&Send"))
self.send_button.setObjectName("send_button")
self.send_button.setDisabled(True);
- self.connect(self.send_button, SIGNAL("clicked()"), self.send)
+ self.send_button.clicked.connect(self.send)
+
+ # Creating the receive button
+ self.receive_button = QPushButton(_("&Receive"))
+ self.receive_button.setObjectName("receive_button")
+ self.receive_button.setDefault(True)
main_layout = QGridLayout(self)
main_layout.addWidget(self.balance_label, 0, 0)
main_layout.addWidget(self.receive_button, 0, 1)
- main_layout.addWidget(self.address_input, 1, 0, 1, -1)
+ main_layout.addWidget(self.address_input, 1, 0)
main_layout.addWidget(self.amount_input, 2, 0)
main_layout.addWidget(self.send_button, 2, 1)
self.history_list.setObjectName("history")
self.history_list.hide()
self.history_list.setAlternatingRowColors(True)
- main_layout.addWidget(self.history_list, 3, 0, 1, -1)
+ main_layout.addWidget(self.history_list, 3, 0, 1, 2)
+
+
+ self.receiving = receiving_widget.ReceivingWidget(self)
+ self.receiving.setObjectName("receiving")
+
+ # Add to the right side
+ self.receiving_box = QGroupBox(_("Select a receiving address"))
+ extra_layout = QGridLayout()
+
+ # Checkbox to filter used addresses
+ hide_used = QCheckBox(_('Hide used addresses'))
+ hide_used.setChecked(True)
+ hide_used.stateChanged.connect(self.receiving.toggle_used)
+
+ # Events for receiving addresses
+ self.receiving.clicked.connect(self.receiving.copy_address)
+ self.receiving.itemDoubleClicked.connect(self.receiving.edit_label)
+ self.receiving.itemChanged.connect(self.receiving.update_label)
+
+ # Label
+ extra_layout.addWidget( QLabel(_('Selecting an address will copy it to the clipboard.\nDouble clicking the label will allow you to edit it.') ),0,0)
+
+ extra_layout.addWidget(self.receiving, 1,0)
+ extra_layout.addWidget(hide_used, 2,0)
+ extra_layout.setColumnMinimumWidth(0,200)
+
+ self.receiving_box.setLayout(extra_layout)
+ main_layout.addWidget(self.receiving_box,0,3,-1,3)
+ self.receiving_box.hide()
+
+ self.receive_button.clicked.connect(self.toggle_receiving_layout)
+
+ # Creating the menu bar
menubar = QMenuBar()
electrum_menu = menubar.addMenu(_("&Bitcoin"))
- electrum_menu.addMenu(_("&Servers"))
+
electrum_menu.addSeparator()
- electrum_menu.addAction(_("&Quit"))
+
+ quit_option = electrum_menu.addAction(_("&Quit"))
+ quit_option.triggered.connect(self.close)
view_menu = menubar.addMenu(_("&View"))
- expert_gui = view_menu.addAction(_("&Pro Mode"))
- self.connect(expert_gui, SIGNAL("triggered()"), expand_callback)
- view_menu.addMenu(_("&Themes"))
+ extra_menu = menubar.addMenu(_("&Extra"))
+
+ backup_wallet = extra_menu.addAction( _("&Create wallet backup"))
+ backup_wallet.triggered.connect(self.backup_wallet)
+
+ export_csv = extra_menu.addAction( _("&Export transactions to CSV") )
+ export_csv.triggered.connect(self.actuator.csv_transaction)
+
+ master_key = extra_menu.addAction( _("Copy master public key to clipboard") )
+ master_key.triggered.connect(self.actuator.copy_master_public_key)
+
+ expert_gui = view_menu.addAction(_("&Classic GUI"))
+ expert_gui.triggered.connect(expand_callback)
+ themes_menu = view_menu.addMenu(_("&Themes"))
+ selected_theme = self.actuator.selected_theme()
+ theme_group = QActionGroup(self)
+ for theme_name in self.actuator.theme_names():
+ theme_action = themes_menu.addAction(theme_name)
+ theme_action.setCheckable(True)
+ if selected_theme == theme_name:
+ theme_action.setChecked(True)
+ class SelectThemeFunctor:
+ def __init__(self, theme_name, toggle_theme):
+ self.theme_name = theme_name
+ self.toggle_theme = toggle_theme
+ def __call__(self, checked):
+ if checked:
+ self.toggle_theme(self.theme_name)
+ delegate = SelectThemeFunctor(theme_name, self.toggle_theme)
+ theme_action.toggled.connect(delegate)
+ theme_group.addAction(theme_action)
view_menu.addSeparator()
show_history = view_menu.addAction(_("Show History"))
show_history.setCheckable(True)
- self.connect(show_history, SIGNAL("toggled(bool)"), self.show_history)
+ show_history.toggled.connect(self.show_history)
- settings_menu = menubar.addMenu(_("&Settings"))
- settings_menu.addAction(_("&Configure Electrum"))
-
help_menu = menubar.addMenu(_("&Help"))
- help_menu.addAction(_("&Contents"))
+ the_website = help_menu.addAction(_("&Website"))
+ the_website.triggered.connect(self.the_website)
help_menu.addSeparator()
- help_menu.addAction(_("&Report Bug"))
- help_menu.addAction(_("&About"))
+ report_bug = help_menu.addAction(_("&Report Bug"))
+ report_bug.triggered.connect(self.show_report_bug)
+ show_about = help_menu.addAction(_("&About"))
+ show_about.triggered.connect(self.show_about)
main_layout.setMenuBar(menubar)
+ self.main_layout = main_layout
quit_shortcut = QShortcut(QKeySequence("Ctrl+Q"), self)
- self.connect(quit_shortcut, SIGNAL("activated()"), self.close)
+ quit_shortcut.activated.connect(self.close)
close_shortcut = QShortcut(QKeySequence("Ctrl+W"), self)
- self.connect(close_shortcut, SIGNAL("activated()"), self.close)
+ close_shortcut.activated.connect(self.close)
+ g = self.config.get("winpos-lite",[4, 25, 351, 149])
+ self.setGeometry(g[0], g[1], g[2], g[3])
+
+ show_hist = self.config.get("gui_show_history",False)
+ show_history.setChecked(show_hist)
+ self.show_history(show_hist)
+
self.setWindowIcon(QIcon(":electrum.png"))
self.setWindowTitle("Electrum")
self.setWindowFlags(Qt.Window|Qt.MSWindowsFixedSizeDialogHint)
self.layout().setSizeConstraint(QLayout.SetFixedSize)
self.setObjectName("main_window")
self.show()
-
- def recompute_style(self):
+
+ def toggle_receiving_layout(self):
+ if self.receiving_box.isVisible():
+ self.receiving_box.hide()
+ self.receive_button.setProperty("isActive", False)
+
+ qApp.style().unpolish(self.receive_button)
+ qApp.style().polish(self.receive_button)
+ else:
+ self.receiving_box.show()
+ self.receive_button.setProperty("isActive", 'true')
+
+ qApp.style().unpolish(self.receive_button)
+ qApp.style().polish(self.receive_button)
+
+ def toggle_theme(self, theme_name):
+ old_path = QDir.currentPath()
+ self.actuator.change_theme(theme_name)
+ # Recompute style globally
qApp.style().unpolish(self)
qApp.style().polish(self)
+ QDir.setCurrent(old_path)
def closeEvent(self, event):
+ g = self.geometry()
+ self.config.set_key("winpos-lite", [g.left(),g.top(),g.width(),g.height()],True)
+ self.config.set_key("gui_show_history", self.history_list.isVisible(),True)
+
super(MiniWindow, self).closeEvent(event)
qApp.quit()
pass
def set_quote_currency(self, currency):
+ """Set and display the fiat currency country."""
assert currency in self.quote_currencies
self.quote_currencies.remove(currency)
- self.quote_currencies = [currency] + self.quote_currencies
+ self.quote_currencies.insert(0, currency)
self.refresh_balance()
def change_quote_currency(self):
self.amount_input_changed(self.amount_input.text())
def set_balances(self, btc_balance):
+ """Set the bitcoin balance and update the amount label accordingly."""
self.btc_balance = btc_balance
quote_text = self.create_quote_text(btc_balance)
if quote_text:
quote_text = "(%s)" % quote_text
btc_balance = "%.2f" % (btc_balance / bitcoin(1))
self.balance_label.set_balance_text(btc_balance, quote_text)
- self.setWindowTitle("Electrum - %s BTC" % btc_balance)
+ self.setWindowTitle("Electrum %s - %s BTC" % (electrum_version, btc_balance))
def amount_input_changed(self, amount_text):
+ """Update the number of bitcoins displayed."""
self.check_button_status()
try:
self.balance_label.show_balance()
def create_quote_text(self, btc_balance):
+ """Return a string copy of the amount fiat currency the
+ user has in bitcoins."""
quote_currency = self.quote_currencies[0]
quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
if quote_balance is None:
self.amount_input.setText("")
def check_button_status(self):
- if (self.address_input.property("isValid") is True and
- len(self.amount_input.text()) > 0):
- self.send_button.setDisabled(False)
- else:
- self.send_button.setDisabled(True)
+ """Check that the bitcoin address is valid and that something
+ is entered in the amount before making the send button clickable."""
+ try:
+ value = D(str(self.amount_input.text())) * 10**8
+ except decimal.InvalidOperation:
+ value = None
+ # self.address_input.property(...) returns a qVariant, not a bool.
+ # The == is needed to properly invoke a comparison.
+ if (self.address_input.property("isValid") == True and
+ value is not None and 0 < value <= self.btc_balance):
+ self.send_button.setDisabled(False)
+ else:
+ self.send_button.setDisabled(True)
def address_field_changed(self, address):
+ # label or alias, with address in brackets
+ match2 = re.match("(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>",
+ address)
+ if match2:
+ address = match2.group(2)
+ self.address_input.setText(address)
+
if self.actuator.is_valid(address):
self.check_button_status()
self.address_input.setProperty("isValid", True)
def update_completions(self, completions):
self.address_completions.setStringList(completions)
+
def update_history(self, tx_history):
- for tx in tx_history[-10:]:
- address = tx["dest_address"]
- amount = D(tx["value"]) / 10**8
- self.history_list.append(address, amount)
+ from util import format_satoshis, age
+
+ self.history_list.empty()
+
+ for item in tx_history[-10:]:
+ tx_hash, conf, is_mine, value, fee, balance, timestamp = item
+ label = self.actuator.wallet.get_label(tx_hash)[0]
+ #amount = D(value) / 10**8
+ v_str = format_satoshis(value, True)
+ self.history_list.append(label, v_str, age(timestamp))
def acceptbit(self):
self.actuator.acceptbit(self.quote_currencies[0])
+ def the_website(self):
+ webbrowser.open("http://electrum-desktop.com")
+
def show_about(self):
QMessageBox.about(self, "Electrum",
_("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."))
def show_report_bug(self):
QMessageBox.information(self, "Electrum - " + _("Reporting Bugs"),
- _("Email bug reports to %s") % "genjix" + "@" + "riseup.net")
+ _("Please report any bugs as issues on github: <a href=\"https://github.com/spesmilo/electrum/issues\">https://github.com/spesmilo/electrum/issues</a>"))
def show_history(self, toggle_state):
if toggle_state:
+ self.main_layout.setRowMinimumHeight(3,200)
self.history_list.show()
else:
+ self.main_layout.setRowMinimumHeight(3,0)
self.history_list.hide()
+ def backup_wallet(self):
+ try:
+ folderName = QFileDialog.getExistingDirectory(QWidget(), 'Select folder to save a copy of your wallet to', os.path.expanduser('~/'))
+ if folderName:
+ sourceFile = util.user_dir() + '/electrum.dat'
+ shutil.copy2(sourceFile, str(folderName))
+ QMessageBox.information(None,"Wallet backup created", "A copy of your wallet file was created in '%s'" % str(folderName))
+ except (IOError, os.error), reason:
+ QMessageBox.critical(None,"Unable to create backup", "Electrum was unable copy your wallet file to the specified location.\n" + str(reason))
+
+
+
+
class BalanceLabel(QLabel):
SHOW_CONNECTING = 1
self.amount_text = ""
def mousePressEvent(self, event):
+ """Change the fiat currency selection if window background is clicked."""
if self.state != self.SHOW_CONNECTING:
self.change_quote_currency()
def set_balance_text(self, btc_balance, quote_text):
+ """Set the amount of bitcoins in the gui."""
if self.state == self.SHOW_CONNECTING:
self.state = self.SHOW_BALANCE
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)
self.setMouseTracking(True)
self.setWindowTitle("Electrum - " + _("Receive Bitcoin payment"))
- self.setWindowFlags(Qt.Window|Qt.FramelessWindowHint|Qt.MSWindowsFixedSizeDialogHint)
+ self.setWindowFlags(Qt.Window|Qt.FramelessWindowHint|
+ Qt.MSWindowsFixedSizeDialogHint)
self.layout().setSizeConstraint(QLayout.SetFixedSize)
#self.setFrameStyle(QFrame.WinPanel|QFrame.Raised)
#self.setAlignment(Qt.AlignCenter)
self.show()
class MiniActuator:
-
+ """Initialize the definitions relating to themes and
+ sending/recieving bitcoins."""
+
+
def __init__(self, wallet):
+ """Retrieve the gui theme used in previous session."""
self.wallet = wallet
+ self.theme_name = self.wallet.config.get('litegui_theme','Cleanlook')
+ self.themes = load_theme_paths()
+ def load_theme(self):
+ """Load theme retrieved from wallet file."""
+ try:
+ theme_prefix, theme_path = self.themes[self.theme_name]
+ except KeyError:
+ util.print_error("Theme not found!", self.theme_name)
+ return
+ QDir.setCurrent(os.path.join(theme_prefix, theme_path))
+ with open(rsrc("style.css")) as style_file:
+ qApp.setStyleSheet(style_file.read())
+
+ def theme_names(self):
+ """Sort themes."""
+ return sorted(self.themes.keys())
+
+ def selected_theme(self):
+ """Select theme."""
+ return self.theme_name
+
+ def change_theme(self, theme_name):
+ """Change theme."""
+ self.theme_name = theme_name
+ self.wallet.config.set_key('litegui_theme',theme_name)
+ self.load_theme()
+
def set_configured_currency(self, set_quote_currency):
- currency = self.wallet.conversion_currency
+ """Set the inital fiat currency conversion country (USD/EUR/GBP) in
+ the GUI to what it was set to in the wallet."""
+ currency = self.wallet.config.get('conversion_currency')
# currency can be none when Electrum is used for the first
# time and no setting has been created yet.
if currency is not None:
set_quote_currency(currency)
def set_config_currency(self, conversion_currency):
- self.wallet.conversion_currency = conversion_currency
+ """Change the wallet fiat currency country."""
+ self.wallet.config.set_key('conversion_currency',conversion_currency,True)
def copy_address(self, receive_popup):
+ """Copy the wallet addresses into the client."""
addrs = [addr for addr in self.wallet.all_addresses()
if not self.wallet.is_change(addr)]
# Select most recent addresses from gap limit
receive_popup.setup(copied_address)
receive_popup.popup()
+ def waiting_dialog(self, f):
+ s = Timer()
+ s.start()
+ w = QDialog()
+ w.resize(200, 70)
+ w.setWindowTitle('Electrum')
+ l = QLabel('Sending transaction, please wait.')
+ vbox = QVBoxLayout()
+ vbox.addWidget(l)
+ w.setLayout(vbox)
+ w.show()
+ def ff():
+ s = f()
+ if s: l.setText(s)
+ else: w.close()
+ w.connect(s, QtCore.SIGNAL('timersignal'), ff)
+ w.exec_()
+ w.destroy()
+
+ def csv_transaction(self):
+ try:
+ fileName = QFileDialog.getSaveFileName(QWidget(), 'Select file to export your wallet transactions to', os.path.expanduser('~/'), "*.csv")
+ if fileName:
+ with open(fileName, "w+") as csvfile:
+ transaction = csv.writer(csvfile)
+ transaction.writerow(["transaction_hash","label", "confirmations", "value", "fee", "balance", "timestamp"])
+ for item in self.wallet.get_tx_history():
+ tx_hash, confirmations, is_mine, value, fee, balance, timestamp = item
+ if confirmations:
+ if timestamp is not None:
+ try:
+ time_string = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
+ except [RuntimeError, TypeError, NameError] as reason:
+ time_string = "unknown"
+ pass
+ else:
+ time_string = "unknown"
+ else:
+ time_string = "pending"
+
+ if value is not None:
+ value_string = format_satoshis(value, True, self.wallet.num_zeros)
+ else:
+ value_string = '--'
+
+ if fee is not None:
+ fee_string = format_satoshis(fee, True, self.wallet.num_zeros)
+ else:
+ fee_string = '0'
+
+ if tx_hash:
+ label, is_default_label = self.wallet.get_label(tx_hash)
+ else:
+ label = ""
+
+ balance_string = format_satoshis(balance, False, self.wallet.num_zeros)
+ transaction.writerow([tx_hash, label, confirmations, value_string, fee_string, balance_string, time_string])
+ QMessageBox.information(None,"CSV Export created", "Your CSV export has been succesfully created.")
+ except (IOError, os.error), reason:
+ QMessageBox.critical(None,"Unable to create csv", "Electrum was unable to produce a transaction export.\n" + str(reason))
+
def send(self, address, amount, parent_window):
+ """Send bitcoins to the target address."""
dest_address = self.fetch_destination(address)
if dest_address is None or not self.wallet.is_valid(dest_address):
fee = bitcoin(1) / 1000
try:
- tx = self.wallet.mktx(dest_address, amount, "", password, fee)
+ tx = self.wallet.mktx([(dest_address, amount)], "", password, fee)
except BaseException as error:
QMessageBox.warning(parent_window, _('Error'), str(error), _('OK'))
return False
-
- status, message = self.wallet.sendtx(tx)
+
+ h = self.wallet.send_tx(tx)
+
+ self.waiting_dialog(lambda: False if self.wallet.tx_event.isSet() else _("Sending transaction, please wait..."))
+
+ status, message = self.wallet.receive_tx(h)
+
if not status:
+ import tempfile
+ dumpf = tempfile.NamedTemporaryFile(delete=False)
+ dumpf.write(tx)
+ dumpf.close()
+ print "Dumped error tx to", dumpf.name
QMessageBox.warning(parent_window, _('Error'), message, _('OK'))
return False
-
- QMessageBox.information(parent_window, '',
- _('Payment sent.') + '\n' + message, _('OK'))
+
+ TransactionWindow(message, self)
+# QMessageBox.information(parent_window, '',
+# _('Your transaction has been sent.') + '\n' + message, _('OK'))
return True
def fetch_destination(self, address):
return recipient
def is_valid(self, address):
+ """Check if bitcoin address is valid."""
+
return self.wallet.is_valid(address)
+ def copy_master_public_key(self):
+ master_pubkey = self.wallet.master_public_key
+ qApp.clipboard().setText(master_pubkey)
+ QMessageBox.information(None,"Copy succesful", "Your public master key has been copied to your clipboard.")
+
+
def acceptbit(self, currency):
- master_pubkey = self.wallet.master_public_key.encode("hex")
+ master_pubkey = self.wallet.master_public_key
url = "http://acceptbit.com/mpk/%s/%s" % (master_pubkey, currency)
webbrowser.open(url)
+ def show_seed_dialog(self):
+ gui_qt.ElectrumWindow.show_seed_dialog(self.wallet)
+
class MiniDriver(QObject):
INITIALIZING = 0
self.wallet = wallet
self.window = window
- self.wallet.register_callback(self.update_callback)
+ self.wallet.interface.register_callback('updated',self.update_callback)
+ self.wallet.interface.register_callback('connected', self.update_callback)
+ self.wallet.interface.register_callback('disconnected', self.update_callback)
self.state = None
self.initializing()
elif not self.wallet.interface.is_connected:
self.connecting()
- elif not self.wallet.blocks == -1:
- self.connecting()
- elif not self.wallet.is_up_to_date:
+ elif not self.wallet.up_to_date:
self.synchronizing()
else:
self.ready()
tx_history = self.wallet.get_tx_history()
self.window.update_history(tx_history)
+
if __name__ == "__main__":
app = QApplication(sys.argv)
with open(rsrc("style.css")) as style_file: