You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2168 lines
81 KiB
2168 lines
81 KiB
13 years ago
|
#!/usr/bin/env python
|
||
|
#
|
||
|
# Electrum - lightweight Bitcoin client
|
||
|
# Copyright (C) 2012 thomasv@gitorious
|
||
|
#
|
||
|
# This program is free software: you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License as published by
|
||
|
# the Free Software Foundation, either version 3 of the License, or
|
||
|
# (at your option) any later version.
|
||
|
#
|
||
|
# This program is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
12 years ago
|
import sys, time, datetime, re, threading
|
||
12 years ago
|
from i18n import _, set_language
|
||
12 years ago
|
from electrum.util import print_error, print_msg
|
||
|
import os.path, json, ast, traceback
|
||
12 years ago
|
import shutil
|
||
12 years ago
|
import StringIO
|
||
12 years ago
|
|
||
12 years ago
|
|
||
13 years ago
|
try:
|
||
|
import PyQt4
|
||
|
except:
|
||
13 years ago
|
sys.exit("Error: Could not import PyQt4 on Linux systems, you may try 'sudo apt-get install python-qt4'")
|
||
13 years ago
|
|
||
13 years ago
|
from PyQt4.QtGui import *
|
||
13 years ago
|
from PyQt4.QtCore import *
|
||
13 years ago
|
import PyQt4.QtCore as QtCore
|
||
12 years ago
|
|
||
12 years ago
|
from electrum.bitcoin import MIN_RELAY_TX_FEE, is_valid
|
||
13 years ago
|
|
||
13 years ago
|
try:
|
||
|
import icons_rc
|
||
|
except:
|
||
12 years ago
|
sys.exit("Error: Could not import icons_rc.py, please generate it with: 'pyrcc4 icons.qrc -o gui/icons_rc.py'")
|
||
13 years ago
|
|
||
12 years ago
|
from electrum.wallet import format_satoshis
|
||
12 years ago
|
from electrum import Transaction
|
||
12 years ago
|
from electrum import mnemonic
|
||
12 years ago
|
from electrum import util, bitcoin, commands, Interface, Wallet
|
||
|
from electrum import SimpleConfig, Wallet, WalletStorage
|
||
12 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
import bmp, pyqrnative
|
||
12 years ago
|
import exchange_rate
|
||
13 years ago
|
|
||
12 years ago
|
from amountedit import AmountEdit
|
||
12 years ago
|
from network_dialog import NetworkDialog
|
||
|
from qrcodewidget import QRCodeWidget
|
||
12 years ago
|
|
||
13 years ago
|
from decimal import Decimal
|
||
13 years ago
|
|
||
13 years ago
|
import platform
|
||
12 years ago
|
import httplib
|
||
|
import socket
|
||
|
import webbrowser
|
||
12 years ago
|
import csv
|
||
13 years ago
|
|
||
|
if platform.system() == 'Windows':
|
||
|
MONOSPACE_FONT = 'Lucida Console'
|
||
|
elif platform.system() == 'Darwin':
|
||
|
MONOSPACE_FONT = 'Monaco'
|
||
|
else:
|
||
|
MONOSPACE_FONT = 'monospace'
|
||
|
|
||
12 years ago
|
from electrum import ELECTRUM_VERSION
|
||
12 years ago
|
import re
|
||
|
|
||
12 years ago
|
from qt_util import *
|
||
|
|
||
12 years ago
|
|
||
13 years ago
|
class MyTreeWidget(QTreeWidget):
|
||
|
def __init__(self, parent):
|
||
|
QTreeWidget.__init__(self, parent)
|
||
13 years ago
|
def ddfr(item):
|
||
13 years ago
|
if not item: return
|
||
13 years ago
|
for i in range(0,self.viewport().height()/5):
|
||
13 years ago
|
if self.itemAt(QPoint(0,i*5)) == item:
|
||
|
break
|
||
|
else:
|
||
|
return
|
||
13 years ago
|
for j in range(0,30):
|
||
|
if self.itemAt(QPoint(0,i*5 + j)) != item:
|
||
|
break
|
||
|
self.emit(SIGNAL('customContextMenuRequested(const QPoint&)'), QPoint(50, i*5 + j - 1))
|
||
13 years ago
|
|
||
13 years ago
|
self.connect(self, SIGNAL('itemActivated(QTreeWidgetItem*, int)'), ddfr)
|
||
13 years ago
|
|
||
|
|
||
|
|
||
13 years ago
|
|
||
13 years ago
|
class StatusBarButton(QPushButton):
|
||
|
def __init__(self, icon, tooltip, func):
|
||
|
QPushButton.__init__(self, icon, '')
|
||
|
self.setToolTip(tooltip)
|
||
|
self.setFlat(True)
|
||
|
self.setMaximumWidth(25)
|
||
|
self.clicked.connect(func)
|
||
|
self.func = func
|
||
12 years ago
|
self.setIconSize(QSize(25,25))
|
||
13 years ago
|
|
||
|
def keyPressEvent(self, e):
|
||
|
if e.key() == QtCore.Qt.Key_Return:
|
||
|
apply(self.func,())
|
||
|
|
||
13 years ago
|
|
||
13 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
|
||
13 years ago
|
|
||
13 years ago
|
|
||
12 years ago
|
|
||
|
|
||
|
|
||
12 years ago
|
default_column_widths = { "history":[40,140,350,140], "contacts":[350,330], "receive":[[370], [370,200,130]] }
|
||
12 years ago
|
|
||
13 years ago
|
class ElectrumWindow(QMainWindow):
|
||
12 years ago
|
def changeEvent(self, event):
|
||
|
flags = self.windowFlags();
|
||
|
if event and event.type() == QtCore.QEvent.WindowStateChange:
|
||
|
if self.windowState() & QtCore.Qt.WindowMinimized:
|
||
|
self.build_menu(True)
|
||
|
# The only way to toggle the icon in the window managers taskbar is to use the Qt.Tooltip flag
|
||
|
# The problem is that it somehow creates an (in)visible window that will stay active and prevent
|
||
|
# Electrum from closing.
|
||
|
# As for now I have no clue how to implement a proper 'hide to tray' functionality.
|
||
|
# self.setWindowFlags(flags & ~Qt.ToolTip)
|
||
|
elif event.oldState() & QtCore.Qt.WindowMinimized:
|
||
|
self.build_menu(False)
|
||
|
#self.setWindowFlags(flags | Qt.ToolTip)
|
||
|
|
||
|
def build_menu(self, is_hidden = False):
|
||
|
m = QMenu()
|
||
|
if self.isMinimized():
|
||
|
m.addAction(_("Show"), self.showNormal)
|
||
|
else:
|
||
|
m.addAction(_("Hide"), self.showMinimized)
|
||
|
|
||
|
m.addSeparator()
|
||
|
m.addAction(_("Exit Electrum"), self.close)
|
||
|
self.tray.setContextMenu(m)
|
||
|
|
||
|
def tray_activated(self, reason):
|
||
|
if reason == QSystemTrayIcon.DoubleClick:
|
||
|
self.showNormal()
|
||
13 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
def __init__(self, config, network):
|
||
13 years ago
|
QMainWindow.__init__(self)
|
||
12 years ago
|
|
||
|
self.config = config
|
||
12 years ago
|
self.network = network
|
||
12 years ago
|
self.init_plugins()
|
||
|
|
||
12 years ago
|
self._close_electrum = False
|
||
12 years ago
|
self.lite = None
|
||
12 years ago
|
self.current_account = self.config.get("current_account", None)
|
||
12 years ago
|
|
||
12 years ago
|
self.icon = QIcon(os.getcwd() + '/icons/electrum.png')
|
||
12 years ago
|
self.tray = QSystemTrayIcon(self.icon, self)
|
||
|
self.tray.setToolTip('Electrum')
|
||
12 years ago
|
self.tray.activated.connect(self.tray_activated)
|
||
12 years ago
|
|
||
12 years ago
|
self.build_menu()
|
||
12 years ago
|
self.tray.show()
|
||
12 years ago
|
self.create_status_bar()
|
||
|
|
||
12 years ago
|
self.need_update = threading.Event()
|
||
13 years ago
|
|
||
12 years ago
|
self.expert_mode = config.get('classic_expert_mode', False)
|
||
12 years ago
|
self.decimal_point = config.get('decimal_point', 8)
|
||
12 years ago
|
self.num_zeros = int(config.get('num_zeros',0))
|
||
12 years ago
|
|
||
12 years ago
|
set_language(config.get('language'))
|
||
|
|
||
13 years ago
|
self.funds_error = False
|
||
13 years ago
|
self.completions = QStringListModel()
|
||
13 years ago
|
|
||
13 years ago
|
self.tabs = tabs = QTabWidget(self)
|
||
12 years ago
|
self.column_widths = self.config.get("column_widths", default_column_widths )
|
||
13 years ago
|
tabs.addTab(self.create_history_tab(), _('History') )
|
||
12 years ago
|
tabs.addTab(self.create_send_tab(), _('Send') )
|
||
13 years ago
|
tabs.addTab(self.create_receive_tab(), _('Receive') )
|
||
|
tabs.addTab(self.create_contacts_tab(), _('Contacts') )
|
||
12 years ago
|
tabs.addTab(self.create_console_tab(), _('Console') )
|
||
13 years ago
|
tabs.setMinimumSize(600, 400)
|
||
13 years ago
|
tabs.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
||
13 years ago
|
self.setCentralWidget(tabs)
|
||
12 years ago
|
|
||
|
g = self.config.get("winpos-qt",[100, 100, 840, 400])
|
||
13 years ago
|
self.setGeometry(g[0], g[1], g[2], g[3])
|
||
13 years ago
|
|
||
12 years ago
|
self.init_menubar()
|
||
|
|
||
|
QShortcut(QKeySequence("Ctrl+W"), self, self.close)
|
||
12 years ago
|
QShortcut(QKeySequence("Ctrl+R"), self, self.update_wallet)
|
||
12 years ago
|
QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
|
||
|
QShortcut(QKeySequence("Ctrl+PgUp"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() - 1 )%tabs.count() ))
|
||
|
QShortcut(QKeySequence("Ctrl+PgDown"), self, lambda: tabs.setCurrentIndex( (tabs.currentIndex() + 1 )%tabs.count() ))
|
||
|
|
||
|
self.connect(self, QtCore.SIGNAL('update_status'), self.update_status)
|
||
|
self.connect(self, QtCore.SIGNAL('banner_signal'), lambda: self.console.showMessage(self.wallet.interface.banner) )
|
||
12 years ago
|
self.connect(self, QtCore.SIGNAL('transaction_signal'), lambda: self.notify_transactions() )
|
||
12 years ago
|
|
||
12 years ago
|
self.history_list.setFocus(True)
|
||
|
|
||
|
self.exchanger = exchange_rate.Exchanger(self)
|
||
|
self.connect(self, SIGNAL("refresh_balance()"), self.update_wallet)
|
||
|
|
||
|
# dark magic fix by flatfly; https://bitcointalk.org/index.php?topic=73651.msg959913#msg959913
|
||
|
if platform.system() == 'Windows':
|
||
|
n = 3 if self.wallet.seed else 2
|
||
|
tabs.setCurrentIndex (n)
|
||
|
tabs.setCurrentIndex (0)
|
||
|
|
||
12 years ago
|
# plugins that need to change the GUI do it here
|
||
|
self.run_hook('init')
|
||
|
|
||
|
|
||
12 years ago
|
|
||
|
|
||
12 years ago
|
def load_wallet(self, wallet):
|
||
|
import electrum
|
||
|
self.wallet = wallet
|
||
|
|
||
12 years ago
|
self.network.register_callback('updated', lambda: self.need_update.set())
|
||
|
self.network.register_callback('banner', lambda: self.emit(QtCore.SIGNAL('banner_signal')))
|
||
|
self.network.register_callback('disconnected', lambda: self.emit(QtCore.SIGNAL('update_status')))
|
||
|
self.network.register_callback('disconnecting', lambda: self.emit(QtCore.SIGNAL('update_status')))
|
||
|
self.network.register_callback('new_transaction', lambda: self.emit(QtCore.SIGNAL('transaction_signal')))
|
||
12 years ago
|
title = 'Electrum ' + self.wallet.electrum_version + ' - ' + self.wallet.storage.path
|
||
12 years ago
|
if not self.wallet.seed: title += ' [%s]' % (_('seedless'))
|
||
|
self.setWindowTitle( title )
|
||
|
self.update_wallet()
|
||
12 years ago
|
# set initial message
|
||
|
self.console.showMessage(self.wallet.interface.banner)
|
||
12 years ago
|
# Once GUI has been initialized check if we want to announce something since the callback has been called before the GUI was initialized
|
||
|
self.notify_transactions()
|
||
|
|
||
12 years ago
|
# account selector
|
||
12 years ago
|
accounts = self.wallet.get_account_names()
|
||
12 years ago
|
self.account_selector.clear()
|
||
12 years ago
|
if len(accounts) > 1:
|
||
|
self.account_selector.addItems([_("All accounts")] + accounts.values())
|
||
|
self.account_selector.setCurrentIndex(0)
|
||
12 years ago
|
self.account_selector.show()
|
||
|
else:
|
||
|
self.account_selector.hide()
|
||
12 years ago
|
|
||
12 years ago
|
self.new_account.setEnabled(self.wallet.seed_version>4)
|
||
|
|
||
12 years ago
|
self.update_lock_icon()
|
||
|
self.update_buttons_on_seed()
|
||
12 years ago
|
self.update_console()
|
||
12 years ago
|
|
||
12 years ago
|
self.run_hook('load_wallet')
|
||
|
|
||
12 years ago
|
|
||
12 years ago
|
def select_wallet_file(self):
|
||
12 years ago
|
wallet_folder = self.wallet.storage.path
|
||
12 years ago
|
re.sub("(\/\w*.dat)$", "", wallet_folder)
|
||
12 years ago
|
file_name = unicode( QFileDialog.getOpenFileName(self, "Select your wallet file", wallet_folder, "*.dat") )
|
||
12 years ago
|
return file_name
|
||
|
|
||
12 years ago
|
|
||
12 years ago
|
def open_wallet(self):
|
||
12 years ago
|
|
||
|
filename = self.select_wallet_file()
|
||
|
if not filename:
|
||
|
return
|
||
|
|
||
12 years ago
|
storage = WalletStorage({'wallet_path': filename})
|
||
|
if not storage.file_exists:
|
||
12 years ago
|
self.show_message("file not found "+ filename)
|
||
|
return
|
||
|
|
||
|
interface = self.wallet.interface
|
||
12 years ago
|
blockchain = self.wallet.verifier.blockchain
|
||
12 years ago
|
self.wallet.stop_threads()
|
||
12 years ago
|
|
||
12 years ago
|
# create new wallet
|
||
12 years ago
|
wallet = Wallet(storage)
|
||
12 years ago
|
wallet.start_threads(interface, blockchain)
|
||
12 years ago
|
|
||
|
self.load_wallet(wallet)
|
||
|
|
||
12 years ago
|
|
||
|
def new_wallet(self):
|
||
12 years ago
|
import installwizard
|
||
12 years ago
|
|
||
12 years ago
|
wallet_folder = self.wallet.storage.path
|
||
12 years ago
|
re.sub("(\/\w*.dat)$", "", wallet_folder)
|
||
|
filename = self.getSaveFileName("Select your wallet file", wallet_folder, "*.dat")
|
||
|
|
||
12 years ago
|
storage = WalletStorage({'wallet_path': filename})
|
||
|
assert not storage.file_exists
|
||
12 years ago
|
|
||
12 years ago
|
wizard = installwizard.InstallWizard(self.config, self.wallet.interface, self.wallet.verifier.blockchain, storage)
|
||
12 years ago
|
wallet = wizard.run()
|
||
|
if wallet:
|
||
|
self.load_wallet(wallet)
|
||
|
|
||
12 years ago
|
|
||
12 years ago
|
|
||
|
def init_menubar(self):
|
||
12 years ago
|
menubar = QMenuBar()
|
||
|
|
||
12 years ago
|
file_menu = menubar.addMenu(_("&File"))
|
||
|
open_wallet_action = file_menu.addAction(_("&Open"))
|
||
12 years ago
|
open_wallet_action.triggered.connect(self.open_wallet)
|
||
|
|
||
12 years ago
|
new_wallet_action = file_menu.addAction(_("&Create/Restore"))
|
||
12 years ago
|
new_wallet_action.triggered.connect(self.new_wallet)
|
||
12 years ago
|
|
||
12 years ago
|
wallet_backup = file_menu.addAction(_("&Copy"))
|
||
12 years ago
|
wallet_backup.triggered.connect(lambda: backup_wallet(self.wallet.storage.path))
|
||
12 years ago
|
|
||
|
quit_item = file_menu.addAction(_("&Close"))
|
||
|
quit_item.triggered.connect(self.close)
|
||
12 years ago
|
|
||
12 years ago
|
wallet_menu = menubar.addMenu(_("&Wallet"))
|
||
|
|
||
|
# Settings / Preferences are all reserved keywords in OSX using this as work around
|
||
|
preferences_name = _("Electrum preferences") if sys.platform == 'darwin' else _("Preferences")
|
||
|
preferences_menu = wallet_menu.addAction(preferences_name)
|
||
12 years ago
|
preferences_menu.triggered.connect(self.settings_dialog)
|
||
12 years ago
|
|
||
12 years ago
|
wallet_menu.addSeparator()
|
||
|
|
||
|
raw_transaction_menu = wallet_menu.addMenu(_("&Load raw transaction"))
|
||
12 years ago
|
|
||
12 years ago
|
raw_transaction_file = raw_transaction_menu.addAction(_("&From file"))
|
||
12 years ago
|
raw_transaction_file.triggered.connect(self.do_process_from_file)
|
||
|
|
||
12 years ago
|
raw_transaction_text = raw_transaction_menu.addAction(_("&From text"))
|
||
12 years ago
|
raw_transaction_text.triggered.connect(self.do_process_from_text)
|
||
12 years ago
|
|
||
12 years ago
|
csv_transaction_menu = wallet_menu.addMenu(_("&Load CSV transaction"))
|
||
|
|
||
|
csv_transaction_file = csv_transaction_menu.addAction(_("&From file"))
|
||
|
csv_transaction_file.triggered.connect(self.do_process_from_csv_file)
|
||
|
|
||
|
csv_transaction_text = csv_transaction_menu.addAction(_("&From text"))
|
||
|
csv_transaction_text.triggered.connect(self.do_process_from_csv_text)
|
||
|
|
||
12 years ago
|
wallet_menu.addSeparator()
|
||
12 years ago
|
|
||
|
show_menu = wallet_menu.addMenu(_("Show"))
|
||
|
|
||
12 years ago
|
#if self.wallet.seed:
|
||
|
show_seed = show_menu.addAction(_("&Seed"))
|
||
|
show_seed.triggered.connect(self.show_seed_dialog)
|
||
12 years ago
|
|
||
|
show_mpk = show_menu.addAction(_("&Master Public Key"))
|
||
|
show_mpk.triggered.connect(self.show_master_public_key)
|
||
|
|
||
12 years ago
|
wallet_menu.addSeparator()
|
||
|
new_contact = wallet_menu.addAction(_("&New contact"))
|
||
12 years ago
|
new_contact.triggered.connect(self.new_contact_dialog)
|
||
12 years ago
|
|
||
12 years ago
|
self.new_account = wallet_menu.addAction(_("&New account"))
|
||
|
self.new_account.triggered.connect(self.new_account_dialog)
|
||
12 years ago
|
|
||
12 years ago
|
import_menu = menubar.addMenu(_("&Import"))
|
||
|
in_labels = import_menu.addAction(_("&Labels"))
|
||
12 years ago
|
in_labels.triggered.connect(self.do_import_labels)
|
||
|
|
||
|
in_private_keys = import_menu.addAction(_("&Private keys"))
|
||
|
in_private_keys.triggered.connect(self.do_import_privkey)
|
||
12 years ago
|
|
||
|
export_menu = menubar.addMenu(_("&Export"))
|
||
|
ex_private_keys = export_menu.addAction(_("&Private keys"))
|
||
12 years ago
|
ex_private_keys.triggered.connect(self.do_export_privkeys)
|
||
|
|
||
12 years ago
|
ex_history = export_menu.addAction(_("&History"))
|
||
12 years ago
|
ex_history.triggered.connect(self.do_export_history)
|
||
|
|
||
12 years ago
|
ex_labels = export_menu.addAction(_("&Labels"))
|
||
12 years ago
|
ex_labels.triggered.connect(self.do_export_labels)
|
||
12 years ago
|
|
||
|
help_menu = menubar.addMenu(_("&Help"))
|
||
12 years ago
|
doc_open = help_menu.addAction(_("&Documentation"))
|
||
|
doc_open.triggered.connect(lambda: webbrowser.open("http://electrum.org/documentation.html"))
|
||
|
web_open = help_menu.addAction(_("&Official website"))
|
||
|
web_open.triggered.connect(lambda: webbrowser.open("http://electrum.org"))
|
||
12 years ago
|
|
||
|
self.setMenuBar(menubar)
|
||
|
|
||
12 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
def notify_transactions(self):
|
||
12 years ago
|
print_error("Notifying GUI")
|
||
|
if len(self.wallet.interface.pending_transactions_for_notifications) > 0:
|
||
|
# Combine the transactions if there are more then three
|
||
|
tx_amount = len(self.wallet.interface.pending_transactions_for_notifications)
|
||
|
if(tx_amount >= 3):
|
||
|
total_amount = 0
|
||
|
for tx in self.wallet.interface.pending_transactions_for_notifications:
|
||
|
is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx)
|
||
|
if(v > 0):
|
||
|
total_amount += v
|
||
|
|
||
12 years ago
|
self.notify("%s new transactions received. Total amount received in the new transactions %s %s" \
|
||
|
% (tx_amount, self.format_amount(total_amount), self.base_unit()))
|
||
12 years ago
|
|
||
|
self.wallet.interface.pending_transactions_for_notifications = []
|
||
|
else:
|
||
|
for tx in self.wallet.interface.pending_transactions_for_notifications:
|
||
|
if tx:
|
||
|
self.wallet.interface.pending_transactions_for_notifications.remove(tx)
|
||
|
is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx)
|
||
|
if(v > 0):
|
||
12 years ago
|
self.notify("New transaction received. %s %s" % (self.format_amount(v), self.base_unit()))
|
||
12 years ago
|
|
||
|
def notify(self, message):
|
||
12 years ago
|
self.tray.showMessage("Electrum", message, QSystemTrayIcon.Information, 20000)
|
||
12 years ago
|
|
||
|
# plugins
|
||
|
def init_plugins(self):
|
||
12 years ago
|
import imp, pkgutil, __builtin__
|
||
|
if __builtin__.use_local_modules:
|
||
12 years ago
|
fp, pathname, description = imp.find_module('plugins')
|
||
12 years ago
|
plugin_names = [name for a, name, b in pkgutil.iter_modules([pathname])]
|
||
12 years ago
|
plugin_names = filter( lambda name: os.path.exists(os.path.join(pathname,name+'.py')), plugin_names)
|
||
12 years ago
|
imp.load_module('electrum_plugins', fp, pathname, description)
|
||
12 years ago
|
plugins = map(lambda name: imp.load_source('electrum_plugins.'+name, os.path.join(pathname,name+'.py')), plugin_names)
|
||
12 years ago
|
else:
|
||
12 years ago
|
import electrum_plugins
|
||
|
plugin_names = [name for a, name, b in pkgutil.iter_modules(electrum_plugins.__path__)]
|
||
12 years ago
|
plugins = [ __import__('electrum_plugins.'+name, fromlist=['electrum_plugins']) for name in plugin_names]
|
||
12 years ago
|
|
||
12 years ago
|
self.plugins = []
|
||
12 years ago
|
for name, p in zip(plugin_names, plugins):
|
||
12 years ago
|
try:
|
||
12 years ago
|
self.plugins.append( p.Plugin(self, name) )
|
||
12 years ago
|
except:
|
||
|
print_msg("Error:cannot initialize plugin",p)
|
||
|
traceback.print_exc(file=sys.stdout)
|
||
|
|
||
12 years ago
|
|
||
12 years ago
|
def run_hook(self, name, *args):
|
||
12 years ago
|
for p in self.plugins:
|
||
|
if not p.is_enabled():
|
||
|
continue
|
||
|
try:
|
||
|
f = eval('p.'+name)
|
||
|
except:
|
||
|
continue
|
||
12 years ago
|
try:
|
||
|
apply(f, args)
|
||
|
except:
|
||
|
print_error("Plugin error")
|
||
|
traceback.print_exc(file=sys.stdout)
|
||
|
|
||
12 years ago
|
return
|
||
12 years ago
|
|
||
12 years ago
|
|
||
|
|
||
12 years ago
|
def set_label(self, name, text = None):
|
||
|
changed = False
|
||
|
old_text = self.wallet.labels.get(name)
|
||
|
if text:
|
||
|
if old_text != text:
|
||
|
self.wallet.labels[name] = text
|
||
12 years ago
|
self.wallet.storage.put('labels', self.wallet.labels)
|
||
12 years ago
|
changed = True
|
||
|
else:
|
||
|
if old_text:
|
||
|
self.wallet.labels.pop(name)
|
||
|
changed = True
|
||
12 years ago
|
self.run_hook('set_label', name, text, changed)
|
||
12 years ago
|
return changed
|
||
|
|
||
|
|
||
12 years ago
|
# custom wrappers for getOpenFileName and getSaveFileName, that remember the path selected by the user
|
||
|
def getOpenFileName(self, title, filter = None):
|
||
|
directory = self.config.get('io_dir', os.path.expanduser('~'))
|
||
|
fileName = unicode( QFileDialog.getOpenFileName(self, title, directory, filter) )
|
||
|
if fileName and directory != os.path.dirname(fileName):
|
||
|
self.config.set_key('io_dir', os.path.dirname(fileName), True)
|
||
|
return fileName
|
||
|
|
||
|
def getSaveFileName(self, title, filename, filter = None):
|
||
|
directory = self.config.get('io_dir', os.path.expanduser('~'))
|
||
|
path = os.path.join( directory, filename )
|
||
|
fileName = unicode( QFileDialog.getSaveFileName(self, title, path, filter) )
|
||
|
if fileName and directory != os.path.dirname(fileName):
|
||
|
self.config.set_key('io_dir', os.path.dirname(fileName), True)
|
||
|
return fileName
|
||
|
|
||
12 years ago
|
def close(self):
|
||
|
QMainWindow.close(self)
|
||
12 years ago
|
self.run_hook('close_main_window')
|
||
13 years ago
|
|
||
13 years ago
|
def connect_slots(self, sender):
|
||
12 years ago
|
self.connect(sender, QtCore.SIGNAL('timersignal'), self.timer_actions)
|
||
|
self.previous_payto_e=''
|
||
13 years ago
|
|
||
12 years ago
|
def timer_actions(self):
|
||
12 years ago
|
if self.need_update.is_set():
|
||
|
self.update_wallet()
|
||
|
self.need_update.clear()
|
||
12 years ago
|
self.run_hook('timer_actions')
|
||
12 years ago
|
|
||
12 years ago
|
def format_amount(self, x, is_diff=False, whitespaces=False):
|
||
12 years ago
|
return format_satoshis(x, is_diff, self.num_zeros, self.decimal_point, whitespaces)
|
||
12 years ago
|
|
||
|
def read_amount(self, x):
|
||
12 years ago
|
if x in['.', '']: return None
|
||
12 years ago
|
p = pow(10, self.decimal_point)
|
||
|
return int( p * Decimal(x) )
|
||
|
|
||
|
def base_unit(self):
|
||
|
assert self.decimal_point in [5,8]
|
||
|
return "BTC" if self.decimal_point == 8 else "mBTC"
|
||
|
|
||
12 years ago
|
def update_status(self):
|
||
13 years ago
|
if self.wallet.interface and self.wallet.interface.is_connected:
|
||
12 years ago
|
if not self.wallet.up_to_date:
|
||
12 years ago
|
text = _("Synchronizing...")
|
||
13 years ago
|
icon = QIcon(":icons/status_waiting.png")
|
||
13 years ago
|
else:
|
||
12 years ago
|
c, u = self.wallet.get_account_balance(self.current_account)
|
||
12 years ago
|
text = _( "Balance" ) + ": %s "%( self.format_amount(c) ) + self.base_unit()
|
||
12 years ago
|
if u: text += " [%s unconfirmed]"%( self.format_amount(u,True).strip() )
|
||
12 years ago
|
text += self.create_quote_text(Decimal(c+u)/100000000)
|
||
12 years ago
|
self.tray.setToolTip(text)
|
||
13 years ago
|
icon = QIcon(":icons/status_connected.png")
|
||
13 years ago
|
else:
|
||
12 years ago
|
text = _("Not connected")
|
||
13 years ago
|
icon = QIcon(":icons/status_disconnected.png")
|
||
13 years ago
|
|
||
12 years ago
|
self.balance_label.setText(text)
|
||
13 years ago
|
self.status_button.setIcon( icon )
|
||
13 years ago
|
|
||
12 years ago
|
def update_wallet(self):
|
||
|
self.update_status()
|
||
12 years ago
|
if self.wallet.up_to_date or not self.wallet.interface.is_connected:
|
||
13 years ago
|
self.update_history_tab()
|
||
13 years ago
|
self.update_receive_tab()
|
||
|
self.update_contacts_tab()
|
||
13 years ago
|
self.update_completions()
|
||
13 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
def create_quote_text(self, btc_balance):
|
||
|
quote_currency = self.config.get("currency", "None")
|
||
|
quote_balance = self.exchanger.exchange(btc_balance, quote_currency)
|
||
|
if quote_balance is None:
|
||
|
quote_text = ""
|
||
|
else:
|
||
|
quote_text = " (%.2f %s)" % (quote_balance, quote_currency)
|
||
|
return quote_text
|
||
|
|
||
13 years ago
|
def create_history_tab(self):
|
||
13 years ago
|
self.history_list = l = MyTreeWidget(self)
|
||
13 years ago
|
l.setColumnCount(5)
|
||
12 years ago
|
for i,width in enumerate(self.column_widths['history']):
|
||
|
l.setColumnWidth(i, width)
|
||
12 years ago
|
l.setHeaderLabels( [ '', _('Date'), _('Description') , _('Amount'), _('Balance')] )
|
||
13 years ago
|
self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), self.tx_label_clicked)
|
||
|
self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), self.tx_label_changed)
|
||
12 years ago
|
|
||
13 years ago
|
l.setContextMenuPolicy(Qt.CustomContextMenu)
|
||
|
l.customContextMenuRequested.connect(self.create_history_menu)
|
||
|
return l
|
||
|
|
||
12 years ago
|
|
||
13 years ago
|
def create_history_menu(self, position):
|
||
|
self.history_list.selectedIndexes()
|
||
|
item = self.history_list.currentItem()
|
||
13 years ago
|
if not item: return
|
||
12 years ago
|
tx_hash = str(item.data(0, Qt.UserRole).toString())
|
||
12 years ago
|
if not tx_hash: return
|
||
13 years ago
|
menu = QMenu()
|
||
12 years ago
|
#menu.addAction(_("Copy ID to Clipboard"), lambda: self.app.clipboard().setText(tx_hash))
|
||
|
menu.addAction(_("Details"), lambda: self.show_tx_details(self.wallet.transactions.get(tx_hash)))
|
||
13 years ago
|
menu.addAction(_("Edit description"), lambda: self.tx_label_clicked(item,2))
|
||
|
menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
|
||
13 years ago
|
|
||
13 years ago
|
|
||
12 years ago
|
def show_tx_details(self, tx):
|
||
12 years ago
|
dialog = QDialog(self)
|
||
12 years ago
|
dialog.setModal(1)
|
||
|
dialog.setWindowTitle(_("Transaction Details"))
|
||
12 years ago
|
vbox = QVBoxLayout()
|
||
|
dialog.setLayout(vbox)
|
||
|
dialog.setMinimumSize(600,300)
|
||
|
|
||
|
tx_hash = tx.hash()
|
||
|
if tx_hash in self.wallet.transactions.keys():
|
||
12 years ago
|
is_relevant, is_mine, v, fee = self.wallet.get_tx_value(tx)
|
||
12 years ago
|
conf, timestamp = self.wallet.verifier.get_confirmations(tx_hash)
|
||
|
if timestamp:
|
||
|
time_str = datetime.datetime.fromtimestamp(timestamp).isoformat(' ')[:-3]
|
||
|
else:
|
||
|
time_str = 'pending'
|
||
|
else:
|
||
|
is_mine = False
|
||
|
|
||
|
vbox.addWidget(QLabel("Transaction ID:"))
|
||
|
e = QLineEdit(tx_hash)
|
||
|
e.setReadOnly(True)
|
||
|
vbox.addWidget(e)
|
||
|
|
||
|
vbox.addWidget(QLabel("Date: %s"%time_str))
|
||
|
vbox.addWidget(QLabel("Status: %d confirmations"%conf))
|
||
|
if is_mine:
|
||
12 years ago
|
if fee is not None:
|
||
12 years ago
|
vbox.addWidget(QLabel("Amount sent: %s"% self.format_amount(v-fee)))
|
||
|
vbox.addWidget(QLabel("Transaction fee: %s"% self.format_amount(fee)))
|
||
12 years ago
|
else:
|
||
12 years ago
|
vbox.addWidget(QLabel("Amount sent: %s"% self.format_amount(v)))
|
||
12 years ago
|
vbox.addWidget(QLabel("Transaction fee: unknown"))
|
||
|
else:
|
||
12 years ago
|
vbox.addWidget(QLabel("Amount received: %s"% self.format_amount(v)))
|
||
12 years ago
|
|
||
12 years ago
|
vbox.addWidget( self.generate_transaction_information_widget(tx) )
|
||
|
|
||
|
ok_button = QPushButton(_("Close"))
|
||
12 years ago
|
ok_button.setDefault(True)
|
||
|
ok_button.clicked.connect(dialog.accept)
|
||
|
|
||
|
hbox = QHBoxLayout()
|
||
|
hbox.addStretch(1)
|
||
|
hbox.addWidget(ok_button)
|
||
|
vbox.addLayout(hbox)
|
||
|
dialog.exec_()
|
||
13 years ago
|
|
||
13 years ago
|
def tx_label_clicked(self, item, column):
|
||
|
if column==2 and item.isSelected():
|
||
|
self.is_edit=True
|
||
|
item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
|
||
|
self.history_list.editItem( item, column )
|
||
|
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
|
||
|
self.is_edit=False
|
||
|
|
||
|
def tx_label_changed(self, item, column):
|
||
|
if self.is_edit:
|
||
|
return
|
||
|
self.is_edit=True
|
||
12 years ago
|
tx_hash = str(item.data(0, Qt.UserRole).toString())
|
||
12 years ago
|
tx = self.wallet.transactions.get(tx_hash)
|
||
13 years ago
|
text = unicode( item.text(2) )
|
||
12 years ago
|
self.set_label(tx_hash, text)
|
||
13 years ago
|
if text:
|
||
13 years ago
|
item.setForeground(2, QBrush(QColor('black')))
|
||
|
else:
|
||
12 years ago
|
text = self.wallet.get_default_label(tx_hash)
|
||
13 years ago
|
item.setText(2, text)
|
||
|
item.setForeground(2, QBrush(QColor('gray')))
|
||
|
self.is_edit=False
|
||
13 years ago
|
|
||
12 years ago
|
|
||
13 years ago
|
def edit_label(self, is_recv):
|
||
|
l = self.receive_list if is_recv else self.contacts_list
|
||
|
item = l.currentItem()
|
||
|
item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
|
||
12 years ago
|
l.editItem( item, 1 )
|
||
13 years ago
|
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
|
||
|
|
||
12 years ago
|
|
||
|
|
||
13 years ago
|
def address_label_clicked(self, item, column, l, column_addr, column_label):
|
||
12 years ago
|
if column == column_label and item.isSelected():
|
||
12 years ago
|
is_editable = item.data(0, 32).toBool()
|
||
|
if not is_editable:
|
||
|
return
|
||
13 years ago
|
addr = unicode( item.text(column_addr) )
|
||
13 years ago
|
label = unicode( item.text(column_label) )
|
||
13 years ago
|
item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
|
||
|
l.editItem( item, column )
|
||
|
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
|
||
|
|
||
12 years ago
|
|
||
13 years ago
|
def address_label_changed(self, item, column, l, column_addr, column_label):
|
||
12 years ago
|
if column == column_label:
|
||
|
addr = unicode( item.text(column_addr) )
|
||
|
text = unicode( item.text(column_label) )
|
||
12 years ago
|
is_editable = item.data(0, 32).toBool()
|
||
|
if not is_editable:
|
||
|
return
|
||
12 years ago
|
|
||
12 years ago
|
changed = self.set_label(addr, text)
|
||
|
if changed:
|
||
|
self.update_history_tab()
|
||
|
self.update_completions()
|
||
12 years ago
|
|
||
12 years ago
|
self.current_item_changed(item)
|
||
12 years ago
|
|
||
12 years ago
|
self.run_hook('item_changed', item, column)
|
||
12 years ago
|
|
||
|
|
||
12 years ago
|
def current_item_changed(self, a):
|
||
12 years ago
|
self.run_hook('current_item_changed', a)
|
||
12 years ago
|
|
||
13 years ago
|
|
||
13 years ago
|
|
||
13 years ago
|
def update_history_tab(self):
|
||
12 years ago
|
|
||
13 years ago
|
self.history_list.clear()
|
||
12 years ago
|
for item in self.wallet.get_tx_history(self.current_account):
|
||
12 years ago
|
tx_hash, conf, is_mine, value, fee, balance, timestamp = item
|
||
12 years ago
|
if conf > 0:
|
||
12 years ago
|
try:
|
||
12 years ago
|
time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3]
|
||
12 years ago
|
except:
|
||
|
time_str = "unknown"
|
||
12 years ago
|
|
||
|
if conf == -1:
|
||
|
time_str = 'unverified'
|
||
12 years ago
|
icon = QIcon(":icons/unconfirmed.png")
|
||
12 years ago
|
elif conf == 0:
|
||
13 years ago
|
time_str = 'pending'
|
||
13 years ago
|
icon = QIcon(":icons/unconfirmed.png")
|
||
12 years ago
|
elif conf < 6:
|
||
|
icon = QIcon(":icons/clock%d.png"%conf)
|
||
|
else:
|
||
|
icon = QIcon(":icons/confirmed.png")
|
||
13 years ago
|
|
||
12 years ago
|
if value is not None:
|
||
12 years ago
|
v_str = self.format_amount(value, True, whitespaces=True)
|
||
12 years ago
|
else:
|
||
|
v_str = '--'
|
||
|
|
||
12 years ago
|
balance_str = self.format_amount(balance, whitespaces=True)
|
||
12 years ago
|
|
||
|
if tx_hash:
|
||
|
label, is_default_label = self.wallet.get_label(tx_hash)
|
||
|
else:
|
||
|
label = _('Pruned transaction outputs')
|
||
|
is_default_label = False
|
||
|
|
||
|
item = QTreeWidgetItem( [ '', time_str, label, v_str, balance_str] )
|
||
13 years ago
|
item.setFont(2, QFont(MONOSPACE_FONT))
|
||
|
item.setFont(3, QFont(MONOSPACE_FONT))
|
||
|
item.setFont(4, QFont(MONOSPACE_FONT))
|
||
12 years ago
|
if value < 0:
|
||
12 years ago
|
item.setForeground(3, QBrush(QColor("#BC1E1E")))
|
||
12 years ago
|
if tx_hash:
|
||
12 years ago
|
item.setData(0, Qt.UserRole, tx_hash)
|
||
|
item.setToolTip(0, "%d %s\nTxId:%s" % (conf, _('Confirmations'), tx_hash) )
|
||
13 years ago
|
if is_default_label:
|
||
13 years ago
|
item.setForeground(2, QBrush(QColor('grey')))
|
||
13 years ago
|
|
||
|
item.setIcon(0, icon)
|
||
|
self.history_list.insertTopLevelItem(0,item)
|
||
12 years ago
|
|
||
13 years ago
|
|
||
13 years ago
|
self.history_list.setCurrentItem(self.history_list.topLevelItem(0))
|
||
|
|
||
13 years ago
|
|
||
|
def create_send_tab(self):
|
||
13 years ago
|
w = QWidget()
|
||
|
|
||
13 years ago
|
grid = QGridLayout()
|
||
13 years ago
|
grid.setSpacing(8)
|
||
13 years ago
|
grid.setColumnMinimumWidth(3,300)
|
||
13 years ago
|
grid.setColumnStretch(5,1)
|
||
13 years ago
|
|
||
12 years ago
|
|
||
13 years ago
|
self.payto_e = QLineEdit()
|
||
13 years ago
|
grid.addWidget(QLabel(_('Pay to')), 1, 0)
|
||
13 years ago
|
grid.addWidget(self.payto_e, 1, 1, 1, 3)
|
||
12 years ago
|
|
||
13 years ago
|
grid.addWidget(HelpButton(_('Recipient of the funds.') + '\n\n' + _('You may enter a Bitcoin address, a label from your list of contacts (a list of completions will be proposed), or an alias (email-like address that forwards to a Bitcoin address)')), 1, 4)
|
||
13 years ago
|
|
||
13 years ago
|
completer = QCompleter()
|
||
13 years ago
|
completer.setCaseSensitivity(False)
|
||
13 years ago
|
self.payto_e.setCompleter(completer)
|
||
|
completer.setModel(self.completions)
|
||
|
|
||
13 years ago
|
self.message_e = QLineEdit()
|
||
13 years ago
|
grid.addWidget(QLabel(_('Description')), 2, 0)
|
||
13 years ago
|
grid.addWidget(self.message_e, 2, 1, 1, 3)
|
||
13 years ago
|
grid.addWidget(HelpButton(_('Description of the transaction (not mandatory).') + '\n\n' + _('The description is not sent to the recipient of the funds. It is stored in your wallet file, and displayed in the \'History\' tab.')), 2, 4)
|
||
13 years ago
|
|
||
12 years ago
|
self.amount_e = AmountEdit(self.base_unit)
|
||
13 years ago
|
grid.addWidget(QLabel(_('Amount')), 3, 0)
|
||
13 years ago
|
grid.addWidget(self.amount_e, 3, 1, 1, 2)
|
||
13 years ago
|
grid.addWidget(HelpButton(
|
||
|
_('Amount to be sent.') + '\n\n' \
|
||
12 years ago
|
+ _('The amount will be displayed in red if you do not have enough funds in your wallet. Note that if you have frozen some of your addresses, the available funds will be lower than your total balance.') \
|
||
12 years ago
|
+ '\n\n' + _('Keyboard shortcut: type "!" to send all your coins.')), 3, 3)
|
||
13 years ago
|
|
||
12 years ago
|
self.fee_e = AmountEdit(self.base_unit)
|
||
13 years ago
|
grid.addWidget(QLabel(_('Fee')), 4, 0)
|
||
13 years ago
|
grid.addWidget(self.fee_e, 4, 1, 1, 2)
|
||
13 years ago
|
grid.addWidget(HelpButton(
|
||
|
_('Bitcoin transactions are in general not free. A transaction fee is paid by the sender of the funds.') + '\n\n'\
|
||
|
+ _('The amount of fee can be decided freely by the sender. However, transactions with low fees take more time to be processed.') + '\n\n'\
|
||
|
+ _('A suggested fee is automatically added to this field. You may override it. The suggested fee increases with the size of the transaction.')), 4, 3)
|
||
12 years ago
|
|
||
|
|
||
12 years ago
|
self.send_button = EnterButton(_("Send"), self.do_send)
|
||
|
grid.addWidget(self.send_button, 6, 1)
|
||
13 years ago
|
|
||
13 years ago
|
b = EnterButton(_("Clear"),self.do_clear)
|
||
13 years ago
|
grid.addWidget(b, 6, 2)
|
||
13 years ago
|
|
||
13 years ago
|
self.payto_sig = QLabel('')
|
||
13 years ago
|
grid.addWidget(self.payto_sig, 7, 0, 1, 4)
|
||
13 years ago
|
|
||
13 years ago
|
QShortcut(QKeySequence("Up"), w, w.focusPreviousChild)
|
||
|
QShortcut(QKeySequence("Down"), w, w.focusNextChild)
|
||
13 years ago
|
w.setLayout(grid)
|
||
|
|
||
|
w2 = QWidget()
|
||
13 years ago
|
vbox = QVBoxLayout()
|
||
13 years ago
|
vbox.addWidget(w)
|
||
|
vbox.addStretch(1)
|
||
|
w2.setLayout(vbox)
|
||
|
|
||
13 years ago
|
def entry_changed( is_fee ):
|
||
|
self.funds_error = False
|
||
12 years ago
|
|
||
12 years ago
|
if self.amount_e.is_shortcut:
|
||
|
self.amount_e.is_shortcut = False
|
||
12 years ago
|
c, u = self.wallet.get_account_balance(self.current_account)
|
||
|
inputs, total, fee = self.wallet.choose_tx_inputs( c + u, 0, self.current_account)
|
||
12 years ago
|
fee = self.wallet.estimated_fee(inputs)
|
||
|
amount = c + u - fee
|
||
12 years ago
|
self.amount_e.setText( self.format_amount(amount) )
|
||
|
self.fee_e.setText( self.format_amount( fee ) )
|
||
12 years ago
|
return
|
||
|
|
||
12 years ago
|
amount = self.read_amount(str(self.amount_e.text()))
|
||
|
fee = self.read_amount(str(self.fee_e.text()))
|
||
|
|
||
13 years ago
|
if not is_fee: fee = None
|
||
|
if amount is None:
|
||
|
return
|
||
12 years ago
|
inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee, self.current_account )
|
||
13 years ago
|
if not is_fee:
|
||
12 years ago
|
self.fee_e.setText( self.format_amount( fee ) )
|
||
13 years ago
|
if inputs:
|
||
|
palette = QPalette()
|
||
|
palette.setColor(self.amount_e.foregroundRole(), QColor('black'))
|
||
12 years ago
|
text = ""
|
||
13 years ago
|
else:
|
||
|
palette = QPalette()
|
||
|
palette.setColor(self.amount_e.foregroundRole(), QColor('red'))
|
||
|
self.funds_error = True
|
||
12 years ago
|
text = _( "Not enough funds" )
|
||
12 years ago
|
c, u = self.wallet.get_frozen_balance()
|
||
|
if c+u: text += ' (' + self.format_amount(c+u).strip() + self.base_unit() + ' ' +_("are frozen") + ')'
|
||
12 years ago
|
|
||
|
self.statusBar().showMessage(text)
|
||
13 years ago
|
self.amount_e.setPalette(palette)
|
||
|
self.fee_e.setPalette(palette)
|
||
|
|
||
|
self.amount_e.textChanged.connect(lambda: entry_changed(False) )
|
||
|
self.fee_e.textChanged.connect(lambda: entry_changed(True) )
|
||
|
|
||
12 years ago
|
self.run_hook('create_send_tab', grid)
|
||
13 years ago
|
return w2
|
||
13 years ago
|
|
||
13 years ago
|
|
||
|
def update_completions(self):
|
||
|
l = []
|
||
|
for addr,label in self.wallet.labels.items():
|
||
|
if addr in self.wallet.addressbook:
|
||
|
l.append( label + ' <' + addr + '>')
|
||
|
|
||
12 years ago
|
self.run_hook('update_completions', l)
|
||
13 years ago
|
self.completions.setStringList(l)
|
||
|
|
||
|
|
||
12 years ago
|
def protected(func):
|
||
|
return lambda s, *args: s.do_protect(func, args)
|
||
13 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
def do_send(self):
|
||
13 years ago
|
|
||
13 years ago
|
label = unicode( self.message_e.text() )
|
||
|
r = unicode( self.payto_e.text() )
|
||
13 years ago
|
r = r.strip()
|
||
|
|
||
13 years ago
|
# label or alias, with address in brackets
|
||
12 years ago
|
m = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
|
||
|
to_address = m.group(2) if m else r
|
||
13 years ago
|
|
||
12 years ago
|
if not is_valid(to_address):
|
||
13 years ago
|
QMessageBox.warning(self, _('Error'), _('Invalid Bitcoin Address') + ':\n' + to_address, _('OK'))
|
||
13 years ago
|
return
|
||
|
|
||
|
try:
|
||
12 years ago
|
amount = self.read_amount(unicode( self.amount_e.text()))
|
||
13 years ago
|
except:
|
||
13 years ago
|
QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
|
||
13 years ago
|
return
|
||
|
try:
|
||
12 years ago
|
fee = self.read_amount(unicode( self.fee_e.text()))
|
||
13 years ago
|
except:
|
||
13 years ago
|
QMessageBox.warning(self, _('Error'), _('Invalid Fee'), _('OK'))
|
||
13 years ago
|
return
|
||
|
|
||
12 years ago
|
confirm_amount = self.config.get('confirm_amount', 100000000)
|
||
|
if amount >= confirm_amount:
|
||
|
if not self.question("send %s to %s?"%(self.format_amount(amount) + ' '+ self.base_unit(), to_address)):
|
||
|
return
|
||
|
|
||
|
self.send_tx(to_address, amount, fee, label)
|
||
|
|
||
|
|
||
|
@protected
|
||
|
def send_tx(self, to_address, amount, fee, label, password):
|
||
|
|
||
13 years ago
|
try:
|
||
12 years ago
|
tx = self.wallet.mktx( [(to_address, amount)], password, fee, account=self.current_account)
|
||
13 years ago
|
except BaseException, e:
|
||
12 years ago
|
traceback.print_exc(file=sys.stdout)
|
||
13 years ago
|
self.show_message(str(e))
|
||
13 years ago
|
return
|
||
12 years ago
|
|
||
12 years ago
|
if tx.requires_fee(self.wallet.verifier) and fee < MIN_RELAY_TX_FEE:
|
||
|
QMessageBox.warning(self, _('Error'), _("This transaction requires a higher fee, or it will not be propagated by the network."), _('OK'))
|
||
12 years ago
|
return
|
||
|
|
||
12 years ago
|
self.run_hook('send_tx', tx)
|
||
12 years ago
|
|
||
12 years ago
|
if label:
|
||
12 years ago
|
self.set_label(tx.hash(), label)
|
||
12 years ago
|
|
||
12 years ago
|
if tx.is_complete:
|
||
12 years ago
|
h = self.wallet.send_tx(tx)
|
||
|
waiting_dialog(lambda: False if self.wallet.tx_event.isSet() else _("Please wait..."))
|
||
|
status, msg = self.wallet.receive_tx( h )
|
||
|
if status:
|
||
|
QMessageBox.information(self, '', _('Payment sent.')+'\n'+msg, _('OK'))
|
||
|
self.do_clear()
|
||
|
self.update_contacts_tab()
|
||
|
else:
|
||
|
QMessageBox.warning(self, _('Error'), msg, _('OK'))
|
||
13 years ago
|
else:
|
||
12 years ago
|
filename = label + '.txn' if label else 'unsigned_%s.txn' % (time.mktime(time.gmtime()))
|
||
12 years ago
|
try:
|
||
12 years ago
|
fileName = self.getSaveFileName(_("Select a transaction filename"), filename, "*.txn")
|
||
12 years ago
|
with open(fileName,'w') as f:
|
||
|
f.write(json.dumps(tx.as_dict(),indent=4) + '\n')
|
||
12 years ago
|
QMessageBox.information(self, _('Unsigned transaction created'), _("Unsigned transaction was saved to file:") + " " +fileName, _('OK'))
|
||
12 years ago
|
except:
|
||
|
QMessageBox.warning(self, _('Error'), _('Could not write transaction to file'), _('OK'))
|
||
13 years ago
|
|
||
12 years ago
|
# add recipient to addressbook
|
||
|
if to_address not in self.wallet.addressbook and not self.wallet.is_mine(to_address):
|
||
|
self.wallet.addressbook.append(to_address)
|
||
|
|
||
13 years ago
|
|
||
12 years ago
|
|
||
|
|
||
13 years ago
|
def set_url(self, url):
|
||
12 years ago
|
address, amount, label, message, signature, identity, url = util.parse_url(url)
|
||
12 years ago
|
if self.base_unit() == 'mBTC': amount = str( 1000* Decimal(amount))
|
||
12 years ago
|
|
||
|
if label and self.wallet.labels.get(address) != label:
|
||
|
if self.question('Give label "%s" to address %s ?'%(label,address)):
|
||
|
if address not in self.wallet.addressbook and not self.wallet.is_mine(address):
|
||
|
self.wallet.addressbook.append(address)
|
||
|
self.set_label(address, label)
|
||
|
|
||
|
self.run_hook('set_url', url, self.show_message, self.question)
|
||
|
|
||
13 years ago
|
self.tabs.setCurrentIndex(1)
|
||
12 years ago
|
label = self.wallet.labels.get(address)
|
||
|
m_addr = label + ' <'+ address +'>' if label else address
|
||
13 years ago
|
self.payto_e.setText(m_addr)
|
||
13 years ago
|
|
||
13 years ago
|
self.message_e.setText(message)
|
||
|
self.amount_e.setText(amount)
|
||
|
if identity:
|
||
|
self.set_frozen(self.payto_e,True)
|
||
|
self.set_frozen(self.amount_e,True)
|
||
|
self.set_frozen(self.message_e,True)
|
||
|
self.payto_sig.setText( ' The bitcoin URI was signed by ' + identity )
|
||
|
else:
|
||
|
self.payto_sig.setVisible(False)
|
||
|
|
||
|
def do_clear(self):
|
||
|
self.payto_sig.setVisible(False)
|
||
|
for e in [self.payto_e, self.message_e, self.amount_e, self.fee_e]:
|
||
|
e.setText('')
|
||
|
self.set_frozen(e,False)
|
||
12 years ago
|
self.update_status()
|
||
13 years ago
|
|
||
|
def set_frozen(self,entry,frozen):
|
||
|
if frozen:
|
||
|
entry.setReadOnly(True)
|
||
|
entry.setFrame(False)
|
||
|
palette = QPalette()
|
||
|
palette.setColor(entry.backgroundRole(), QColor('lightgray'))
|
||
|
entry.setPalette(palette)
|
||
|
else:
|
||
|
entry.setReadOnly(False)
|
||
|
entry.setFrame(True)
|
||
|
palette = QPalette()
|
||
|
palette.setColor(entry.backgroundRole(), QColor('white'))
|
||
|
entry.setPalette(palette)
|
||
13 years ago
|
|
||
|
|
||
13 years ago
|
def toggle_freeze(self,addr):
|
||
|
if not addr: return
|
||
|
if addr in self.wallet.frozen_addresses:
|
||
|
self.wallet.unfreeze(addr)
|
||
|
else:
|
||
|
self.wallet.freeze(addr)
|
||
|
self.update_receive_tab()
|
||
|
|
||
|
def toggle_priority(self,addr):
|
||
|
if not addr: return
|
||
|
if addr in self.wallet.prioritized_addresses:
|
||
|
self.wallet.unprioritize(addr)
|
||
|
else:
|
||
|
self.wallet.prioritize(addr)
|
||
|
self.update_receive_tab()
|
||
|
|
||
|
|
||
13 years ago
|
def create_list_tab(self, headers):
|
||
13 years ago
|
"generic tab creation method"
|
||
|
l = MyTreeWidget(self)
|
||
13 years ago
|
l.setColumnCount( len(headers) )
|
||
|
l.setHeaderLabels( headers )
|
||
13 years ago
|
|
||
13 years ago
|
w = QWidget()
|
||
13 years ago
|
vbox = QVBoxLayout()
|
||
13 years ago
|
w.setLayout(vbox)
|
||
|
|
||
13 years ago
|
vbox.setMargin(0)
|
||
13 years ago
|
vbox.setSpacing(0)
|
||
13 years ago
|
vbox.addWidget(l)
|
||
13 years ago
|
buttons = QWidget()
|
||
|
vbox.addWidget(buttons)
|
||
13 years ago
|
|
||
13 years ago
|
hbox = QHBoxLayout()
|
||
13 years ago
|
hbox.setMargin(0)
|
||
13 years ago
|
hbox.setSpacing(0)
|
||
13 years ago
|
buttons.setLayout(hbox)
|
||
|
|
||
13 years ago
|
return l,w,hbox
|
||
13 years ago
|
|
||
13 years ago
|
|
||
13 years ago
|
def create_receive_tab(self):
|
||
12 years ago
|
l,w,hbox = self.create_list_tab([ _('Address'), _('Label'), _('Balance'), _('Tx')])
|
||
13 years ago
|
l.setContextMenuPolicy(Qt.CustomContextMenu)
|
||
|
l.customContextMenuRequested.connect(self.create_receive_menu)
|
||
12 years ago
|
self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,0,1))
|
||
|
self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,0,1))
|
||
12 years ago
|
self.connect(l, SIGNAL('currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)'), lambda a,b: self.current_item_changed(a))
|
||
13 years ago
|
self.receive_list = l
|
||
13 years ago
|
self.receive_buttons_hbox = hbox
|
||
12 years ago
|
hbox.addStretch(1)
|
||
13 years ago
|
return w
|
||
|
|
||
12 years ago
|
|
||
|
def receive_tab_set_mode(self, i):
|
||
12 years ago
|
self.save_column_widths()
|
||
12 years ago
|
self.expert_mode = (i == 1)
|
||
|
self.config.set_key('classic_expert_mode', self.expert_mode, True)
|
||
12 years ago
|
self.update_receive_tab()
|
||
|
|
||
12 years ago
|
|
||
12 years ago
|
def save_column_widths(self):
|
||
12 years ago
|
if not self.expert_mode:
|
||
12 years ago
|
widths = [ self.receive_list.columnWidth(0) ]
|
||
12 years ago
|
else:
|
||
|
widths = []
|
||
|
for i in range(self.receive_list.columnCount() -1):
|
||
|
widths.append(self.receive_list.columnWidth(i))
|
||
12 years ago
|
self.column_widths["receive"][self.expert_mode] = widths
|
||
12 years ago
|
|
||
12 years ago
|
self.column_widths["history"] = []
|
||
12 years ago
|
for i in range(self.history_list.columnCount() - 1):
|
||
12 years ago
|
self.column_widths["history"].append(self.history_list.columnWidth(i))
|
||
12 years ago
|
|
||
12 years ago
|
self.column_widths["contacts"] = []
|
||
12 years ago
|
for i in range(self.contacts_list.columnCount() - 1):
|
||
12 years ago
|
self.column_widths["contacts"].append(self.contacts_list.columnWidth(i))
|
||
13 years ago
|
|
||
12 years ago
|
self.config.set_key("column_widths", self.column_widths, True)
|
||
|
|
||
12 years ago
|
|
||
13 years ago
|
def create_contacts_tab(self):
|
||
13 years ago
|
l,w,hbox = self.create_list_tab([_('Address'), _('Label'), _('Tx')])
|
||
13 years ago
|
l.setContextMenuPolicy(Qt.CustomContextMenu)
|
||
|
l.customContextMenuRequested.connect(self.create_contact_menu)
|
||
12 years ago
|
for i,width in enumerate(self.column_widths['contacts']):
|
||
|
l.setColumnWidth(i, width)
|
||
|
|
||
13 years ago
|
self.connect(l, SIGNAL('itemDoubleClicked(QTreeWidgetItem*, int)'), lambda a, b: self.address_label_clicked(a,b,l,0,1))
|
||
|
self.connect(l, SIGNAL('itemChanged(QTreeWidgetItem*, int)'), lambda a,b: self.address_label_changed(a,b,l,0,1))
|
||
13 years ago
|
self.contacts_list = l
|
||
13 years ago
|
self.contacts_buttons_hbox = hbox
|
||
13 years ago
|
hbox.addStretch(1)
|
||
13 years ago
|
return w
|
||
13 years ago
|
|
||
13 years ago
|
|
||
12 years ago
|
def delete_imported_key(self, addr):
|
||
12 years ago
|
if self.question(_("Do you want to remove")+" %s "%addr +_("from your wallet?")):
|
||
12 years ago
|
self.wallet.delete_imported_key(addr)
|
||
12 years ago
|
self.update_receive_tab()
|
||
|
self.update_history_tab()
|
||
|
|
||
|
|
||
13 years ago
|
def create_receive_menu(self, position):
|
||
|
# fixme: this function apparently has a side effect.
|
||
|
# if it is not called the menu pops up several times
|
||
13 years ago
|
#self.receive_list.selectedIndexes()
|
||
|
|
||
13 years ago
|
item = self.receive_list.itemAt(position)
|
||
13 years ago
|
if not item: return
|
||
12 years ago
|
addr = unicode(item.text(0))
|
||
12 years ago
|
if not is_valid(addr):
|
||
|
item.setExpanded(not item.isExpanded())
|
||
|
return
|
||
13 years ago
|
menu = QMenu()
|
||
12 years ago
|
menu.addAction(_("Copy to clipboard"), lambda: self.app.clipboard().setText(addr))
|
||
12 years ago
|
menu.addAction(_("QR code"), lambda: self.show_qrcode("bitcoin:" + addr, _("Address")) )
|
||
13 years ago
|
menu.addAction(_("Edit label"), lambda: self.edit_label(True))
|
||
12 years ago
|
menu.addAction(_("Private key"), lambda: self.show_private_key(addr))
|
||
12 years ago
|
menu.addAction(_("Sign message"), lambda: self.sign_message(addr))
|
||
12 years ago
|
if addr in self.wallet.imported_keys:
|
||
|
menu.addAction(_("Remove from wallet"), lambda: self.delete_imported_key(addr))
|
||
12 years ago
|
|
||
12 years ago
|
if self.expert_mode:
|
||
12 years ago
|
t = _("Unfreeze") if addr in self.wallet.frozen_addresses else _("Freeze")
|
||
|
menu.addAction(t, lambda: self.toggle_freeze(addr))
|
||
|
t = _("Unprioritize") if addr in self.wallet.prioritized_addresses else _("Prioritize")
|
||
|
menu.addAction(t, lambda: self.toggle_priority(addr))
|
||
|
|
||
12 years ago
|
self.run_hook('receive_menu', menu)
|
||
13 years ago
|
menu.exec_(self.receive_list.viewport().mapToGlobal(position))
|
||
|
|
||
|
|
||
12 years ago
|
def payto(self, addr):
|
||
|
if not addr: return
|
||
|
label = self.wallet.labels.get(addr)
|
||
|
m_addr = label + ' <' + addr + '>' if label else addr
|
||
13 years ago
|
self.tabs.setCurrentIndex(1)
|
||
13 years ago
|
self.payto_e.setText(m_addr)
|
||
13 years ago
|
self.amount_e.setFocus()
|
||
|
|
||
12 years ago
|
|
||
|
def delete_contact(self, x):
|
||
12 years ago
|
if self.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")):
|
||
12 years ago
|
self.wallet.delete_contact(x)
|
||
|
self.set_label(x, None)
|
||
|
self.update_history_tab()
|
||
|
self.update_contacts_tab()
|
||
|
self.update_completions()
|
||
13 years ago
|
|
||
13 years ago
|
|
||
12 years ago
|
def create_contact_menu(self, position):
|
||
13 years ago
|
item = self.contacts_list.itemAt(position)
|
||
13 years ago
|
if not item: return
|
||
13 years ago
|
addr = unicode(item.text(0))
|
||
13 years ago
|
label = unicode(item.text(1))
|
||
12 years ago
|
is_editable = item.data(0,32).toBool()
|
||
|
payto_addr = item.data(0,33).toString()
|
||
13 years ago
|
menu = QMenu()
|
||
|
menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
|
||
12 years ago
|
menu.addAction(_("Pay to"), lambda: self.payto(payto_addr))
|
||
12 years ago
|
menu.addAction(_("QR code"), lambda: self.show_qrcode("bitcoin:" + addr, _("Address")))
|
||
12 years ago
|
if is_editable:
|
||
13 years ago
|
menu.addAction(_("Edit label"), lambda: self.edit_label(False))
|
||
12 years ago
|
menu.addAction(_("Delete"), lambda: self.delete_contact(addr))
|
||
|
|
||
|
self.run_hook('create_contact_menu', menu, item)
|
||
13 years ago
|
menu.exec_(self.contacts_list.viewport().mapToGlobal(position))
|
||
13 years ago
|
|
||
|
|
||
12 years ago
|
def update_receive_item(self, item):
|
||
12 years ago
|
item.setFont(0, QFont(MONOSPACE_FONT))
|
||
12 years ago
|
address = str(item.data(0,0).toString())
|
||
12 years ago
|
label = self.wallet.labels.get(address,'')
|
||
12 years ago
|
item.setData(1,0,label)
|
||
12 years ago
|
item.setData(0,32, True) # is editable
|
||
12 years ago
|
|
||
12 years ago
|
self.run_hook('update_receive_item', address, item)
|
||
12 years ago
|
|
||
12 years ago
|
c, u = self.wallet.get_addr_balance(address)
|
||
12 years ago
|
balance = self.format_amount(c + u)
|
||
12 years ago
|
item.setData(2,0,balance)
|
||
12 years ago
|
|
||
12 years ago
|
if self.expert_mode:
|
||
12 years ago
|
if address in self.wallet.frozen_addresses:
|
||
12 years ago
|
item.setBackgroundColor(0, QColor('lightblue'))
|
||
12 years ago
|
elif address in self.wallet.prioritized_addresses:
|
||
12 years ago
|
item.setBackgroundColor(0, QColor('lightgreen'))
|
||
12 years ago
|
|
||
|
|
||
13 years ago
|
def update_receive_tab(self):
|
||
13 years ago
|
l = self.receive_list
|
||
12 years ago
|
|
||
13 years ago
|
l.clear()
|
||
12 years ago
|
l.setColumnHidden(2, not self.expert_mode)
|
||
12 years ago
|
l.setColumnHidden(3, not self.expert_mode)
|
||
12 years ago
|
for i,width in enumerate(self.column_widths['receive'][self.expert_mode]):
|
||
|
l.setColumnWidth(i, width)
|
||
13 years ago
|
|
||
12 years ago
|
if self.current_account is None:
|
||
|
account_items = self.wallet.accounts.items()
|
||
|
elif self.current_account != -1:
|
||
|
account_items = [(self.current_account, self.wallet.accounts.get(self.current_account))]
|
||
|
else:
|
||
|
account_items = []
|
||
13 years ago
|
|
||
12 years ago
|
for k, account in account_items:
|
||
12 years ago
|
name = self.wallet.get_account_name(k)
|
||
12 years ago
|
c,u = self.wallet.get_account_balance(k)
|
||
12 years ago
|
account_item = QTreeWidgetItem( [ name, '', self.format_amount(c+u), ''] )
|
||
12 years ago
|
l.addTopLevelItem(account_item)
|
||
|
account_item.setExpanded(True)
|
||
12 years ago
|
|
||
12 years ago
|
for is_change in ([0,1] if self.expert_mode else [0]):
|
||
|
if self.expert_mode:
|
||
|
name = "Receiving" if not is_change else "Change"
|
||
|
seq_item = QTreeWidgetItem( [ name, '', '', '', ''] )
|
||
|
account_item.addChild(seq_item)
|
||
|
if not is_change: seq_item.setExpanded(True)
|
||
|
else:
|
||
|
seq_item = account_item
|
||
12 years ago
|
is_red = False
|
||
|
gap = 0
|
||
13 years ago
|
|
||
12 years ago
|
for address in account.get_addresses(is_change):
|
||
12 years ago
|
h = self.wallet.history.get(address,[])
|
||
|
|
||
12 years ago
|
if h == []:
|
||
|
gap += 1
|
||
|
if gap > self.wallet.gap_limit:
|
||
|
is_red = True
|
||
|
else:
|
||
|
gap = 0
|
||
12 years ago
|
|
||
|
num_tx = '*' if h == ['*'] else "%d"%len(h)
|
||
12 years ago
|
item = QTreeWidgetItem( [ address, '', '', num_tx] )
|
||
12 years ago
|
self.update_receive_item(item)
|
||
12 years ago
|
if is_red:
|
||
12 years ago
|
item.setBackgroundColor(1, QColor('red'))
|
||
|
seq_item.addChild(item)
|
||
|
|
||
12 years ago
|
|
||
|
if self.wallet.imported_keys and (self.current_account is None or self.current_account == -1):
|
||
12 years ago
|
c,u = self.wallet.get_imported_balance()
|
||
12 years ago
|
account_item = QTreeWidgetItem( [ _('Imported'), '', self.format_amount(c+u), ''] )
|
||
12 years ago
|
l.addTopLevelItem(account_item)
|
||
|
account_item.setExpanded(True)
|
||
|
for address in self.wallet.imported_keys.keys():
|
||
12 years ago
|
item = QTreeWidgetItem( [ address, '', '', ''] )
|
||
12 years ago
|
self.update_receive_item(item)
|
||
|
account_item.addChild(item)
|
||
|
|
||
13 years ago
|
|
||
13 years ago
|
# we use column 1 because column 0 may be hidden
|
||
|
l.setCurrentItem(l.topLevelItem(0),1)
|
||
13 years ago
|
|
||
13 years ago
|
|
||
13 years ago
|
def update_contacts_tab(self):
|
||
13 years ago
|
l = self.contacts_list
|
||
|
l.clear()
|
||
13 years ago
|
|
||
13 years ago
|
for address in self.wallet.addressbook:
|
||
|
label = self.wallet.labels.get(address,'')
|
||
12 years ago
|
n = self.wallet.get_num_tx(address)
|
||
12 years ago
|
item = QTreeWidgetItem( [ address, label, "%d"%n] )
|
||
13 years ago
|
item.setFont(0, QFont(MONOSPACE_FONT))
|
||
12 years ago
|
# 32 = label can be edited (bool)
|
||
|
item.setData(0,32, True)
|
||
|
# 33 = payto string
|
||
|
item.setData(0,33, address)
|
||
13 years ago
|
l.addTopLevelItem(item)
|
||
13 years ago
|
|
||
12 years ago
|
self.run_hook('update_contacts_tab', l)
|
||
13 years ago
|
l.setCurrentItem(l.topLevelItem(0))
|
||
13 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
def create_console_tab(self):
|
||
12 years ago
|
from qt_console import Console
|
||
12 years ago
|
self.console = console = Console()
|
||
12 years ago
|
return console
|
||
|
|
||
12 years ago
|
|
||
|
def update_console(self):
|
||
|
console = self.console
|
||
|
console.history = self.config.get("console-history",[])
|
||
|
console.history_index = len(console.history)
|
||
12 years ago
|
|
||
12 years ago
|
console.updateNamespace({'wallet' : self.wallet, 'network' : self.network, 'gui':self})
|
||
12 years ago
|
console.updateNamespace({'util' : util, 'bitcoin':bitcoin})
|
||
12 years ago
|
|
||
12 years ago
|
c = commands.Commands(self.wallet, self.wallet.interface, lambda: self.console.set_json(True))
|
||
12 years ago
|
methods = {}
|
||
|
def mkfunc(f, method):
|
||
|
return lambda *args: apply( f, (method, args, self.password_dialog ))
|
||
|
for m in dir(c):
|
||
|
if m[0]=='_' or m=='wallet' or m == 'interface': continue
|
||
|
methods[m] = mkfunc(c._run, m)
|
||
|
|
||
|
console.updateNamespace(methods)
|
||
12 years ago
|
|
||
13 years ago
|
|
||
12 years ago
|
def change_account(self,s):
|
||
|
if s == _("All accounts"):
|
||
|
self.current_account = None
|
||
|
else:
|
||
12 years ago
|
accounts = self.wallet.get_account_names()
|
||
12 years ago
|
for k, v in accounts.items():
|
||
|
if v == s:
|
||
|
self.current_account = k
|
||
|
self.update_history_tab()
|
||
|
self.update_status()
|
||
|
self.update_receive_tab()
|
||
12 years ago
|
|
||
13 years ago
|
def create_status_bar(self):
|
||
12 years ago
|
|
||
13 years ago
|
sb = QStatusBar()
|
||
13 years ago
|
sb.setFixedHeight(35)
|
||
12 years ago
|
qtVersion = qVersion()
|
||
12 years ago
|
|
||
12 years ago
|
self.balance_label = QLabel("")
|
||
|
sb.addWidget(self.balance_label)
|
||
|
|
||
12 years ago
|
from version_getter import UpdateLabel
|
||
12 years ago
|
self.updatelabel = UpdateLabel(self.config, sb)
|
||
12 years ago
|
|
||
12 years ago
|
self.account_selector = QComboBox()
|
||
|
self.connect(self.account_selector,SIGNAL("activated(QString)"),self.change_account)
|
||
|
sb.addPermanentWidget(self.account_selector)
|
||
12 years ago
|
|
||
12 years ago
|
if (int(qtVersion[0]) >= 4 and int(qtVersion[2]) >= 7):
|
||
12 years ago
|
sb.addPermanentWidget( StatusBarButton( QIcon(":icons/switchgui.png"), _("Switch to Lite Mode"), self.go_lite ) )
|
||
12 years ago
|
|
||
|
self.lock_icon = QIcon()
|
||
|
self.password_button = StatusBarButton( self.lock_icon, _("Password"), self.change_password_dialog )
|
||
|
sb.addPermanentWidget( self.password_button )
|
||
|
|
||
12 years ago
|
sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), _("Preferences"), self.settings_dialog ) )
|
||
12 years ago
|
self.seed_button = StatusBarButton( QIcon(":icons/seed.png"), _("Seed"), self.show_seed_dialog )
|
||
|
sb.addPermanentWidget( self.seed_button )
|
||
12 years ago
|
self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), _("Network"), self.run_network_dialog )
|
||
13 years ago
|
sb.addPermanentWidget( self.status_button )
|
||
12 years ago
|
|
||
12 years ago
|
self.run_hook('create_status_bar', (sb,))
|
||
|
|
||
13 years ago
|
self.setStatusBar(sb)
|
||
12 years ago
|
|
||
|
|
||
12 years ago
|
def update_lock_icon(self):
|
||
|
icon = QIcon(":icons/lock.png") if self.wallet.use_encryption else QIcon(":icons/unlock.png")
|
||
|
self.password_button.setIcon( icon )
|
||
|
|
||
|
|
||
|
def update_buttons_on_seed(self):
|
||
|
if self.wallet.seed:
|
||
|
self.seed_button.show()
|
||
|
self.password_button.show()
|
||
|
self.send_button.setText(_("Send"))
|
||
|
else:
|
||
|
self.password_button.hide()
|
||
|
self.seed_button.hide()
|
||
|
self.send_button.setText(_("Create unsigned transaction"))
|
||
|
|
||
|
|
||
12 years ago
|
def change_password_dialog(self):
|
||
|
from password_dialog import PasswordDialog
|
||
|
d = PasswordDialog(self.wallet, self)
|
||
|
d.run()
|
||
12 years ago
|
self.update_lock_icon()
|
||
12 years ago
|
|
||
12 years ago
|
|
||
|
def go_lite(self):
|
||
12 years ago
|
import lite_window
|
||
12 years ago
|
self.config.set_key('gui', 'lite', True)
|
||
12 years ago
|
self.hide()
|
||
|
if self.lite:
|
||
|
self.lite.mini.show()
|
||
|
else:
|
||
12 years ago
|
self.lite = lite_window.ElectrumGui(self.config, None, None, self)
|
||
12 years ago
|
self.lite.main(None)
|
||
13 years ago
|
|
||
12 years ago
|
|
||
13 years ago
|
def new_contact_dialog(self):
|
||
13 years ago
|
text, ok = QInputDialog.getText(self, _('New Contact'), _('Address') + ':')
|
||
13 years ago
|
address = unicode(text)
|
||
13 years ago
|
if ok:
|
||
12 years ago
|
if is_valid(address):
|
||
12 years ago
|
self.wallet.add_contact(address)
|
||
13 years ago
|
self.update_contacts_tab()
|
||
13 years ago
|
self.update_history_tab()
|
||
|
self.update_completions()
|
||
13 years ago
|
else:
|
||
13 years ago
|
QMessageBox.warning(self, _('Error'), _('Invalid Address'), _('OK'))
|
||
13 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
def new_account_dialog(self):
|
||
12 years ago
|
|
||
|
dialog = QDialog(self)
|
||
|
dialog.setModal(1)
|
||
|
dialog.setWindowTitle(_("New Account"))
|
||
|
|
||
|
addr = self.wallet.new_account_address()
|
||
|
vbox = QVBoxLayout()
|
||
12 years ago
|
vbox.addWidget(QLabel(_("To create a new account, please send coins to the first address of that account:")))
|
||
12 years ago
|
e = QLineEdit(addr)
|
||
|
e.setReadOnly(True)
|
||
|
vbox.addWidget(e)
|
||
|
|
||
|
ok_button = QPushButton(_("OK"))
|
||
|
ok_button.setDefault(True)
|
||
|
ok_button.clicked.connect(dialog.accept)
|
||
|
|
||
|
hbox = QHBoxLayout()
|
||
|
hbox.addStretch(1)
|
||
|
hbox.addWidget(ok_button)
|
||
|
vbox.addLayout(hbox)
|
||
|
|
||
|
dialog.setLayout(vbox)
|
||
|
dialog.exec_()
|
||
|
|
||
12 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
def show_master_public_key(self):
|
||
12 years ago
|
dialog = QDialog(self)
|
||
12 years ago
|
dialog.setModal(1)
|
||
12 years ago
|
dialog.setWindowTitle(_("Master Public Key"))
|
||
12 years ago
|
|
||
|
main_text = QTextEdit()
|
||
12 years ago
|
main_text.setText(self.wallet.get_master_public_key())
|
||
12 years ago
|
main_text.setReadOnly(True)
|
||
12 years ago
|
main_text.setMaximumHeight(170)
|
||
12 years ago
|
qrw = QRCodeWidget(self.wallet.get_master_public_key())
|
||
12 years ago
|
|
||
|
ok_button = QPushButton(_("OK"))
|
||
|
ok_button.setDefault(True)
|
||
|
ok_button.clicked.connect(dialog.accept)
|
||
|
|
||
|
main_layout = QGridLayout()
|
||
12 years ago
|
main_layout.addWidget(QLabel(_('Your Master Public Key is:')), 0, 0, 1, 2)
|
||
12 years ago
|
|
||
12 years ago
|
main_layout.addWidget(main_text, 1, 0)
|
||
|
main_layout.addWidget(qrw, 1, 1 )
|
||
12 years ago
|
|
||
12 years ago
|
vbox = QVBoxLayout()
|
||
|
vbox.addLayout(main_layout)
|
||
|
hbox = QHBoxLayout()
|
||
|
hbox.addStretch(1)
|
||
|
hbox.addWidget(ok_button)
|
||
|
vbox.addLayout(hbox)
|
||
12 years ago
|
|
||
12 years ago
|
dialog.setLayout(vbox)
|
||
12 years ago
|
dialog.exec_()
|
||
|
|
||
|
|
||
12 years ago
|
@protected
|
||
|
def show_seed_dialog(self, password):
|
||
|
if not self.wallet.seed:
|
||
12 years ago
|
QMessageBox.information(parent, _('Message'), _('No seed'), _('OK'))
|
||
13 years ago
|
return
|
||
13 years ago
|
try:
|
||
12 years ago
|
seed = self.wallet.decode_seed(password)
|
||
13 years ago
|
except:
|
||
12 years ago
|
QMessageBox.warning(self, _('Error'), _('Incorrect Password'), _('OK'))
|
||
13 years ago
|
return
|
||
12 years ago
|
|
||
12 years ago
|
from seed_dialog import SeedDialog
|
||
|
d = SeedDialog(self)
|
||
|
d.show_seed(seed, self.wallet.imported_keys)
|
||
12 years ago
|
|
||
13 years ago
|
|
||
13 years ago
|
|
||
12 years ago
|
def show_qrcode(self, data, title = "QR code"):
|
||
12 years ago
|
if not data: return
|
||
12 years ago
|
d = QDialog(self)
|
||
13 years ago
|
d.setModal(1)
|
||
12 years ago
|
d.setWindowTitle(title)
|
||
13 years ago
|
d.setMinimumSize(270, 300)
|
||
|
vbox = QVBoxLayout()
|
||
12 years ago
|
qrw = QRCodeWidget(data)
|
||
12 years ago
|
vbox.addWidget(qrw, 1)
|
||
|
vbox.addWidget(QLabel(data), 0, Qt.AlignHCenter)
|
||
13 years ago
|
hbox = QHBoxLayout()
|
||
|
hbox.addStretch(1)
|
||
12 years ago
|
|
||
|
def print_qr(self):
|
||
|
filename = "qrcode.bmp"
|
||
|
bmp.save_qrcode(qrw.qr, filename)
|
||
|
QMessageBox.information(None, _('Message'), _("QR code saved to file") + " " + filename, _('OK'))
|
||
|
|
||
12 years ago
|
b = QPushButton(_("Save"))
|
||
12 years ago
|
hbox.addWidget(b)
|
||
|
b.clicked.connect(print_qr)
|
||
|
|
||
|
b = QPushButton(_("Close"))
|
||
13 years ago
|
hbox.addWidget(b)
|
||
|
b.clicked.connect(d.accept)
|
||
12 years ago
|
b.setDefault(True)
|
||
13 years ago
|
|
||
|
vbox.addLayout(hbox)
|
||
|
d.setLayout(vbox)
|
||
|
d.exec_()
|
||
|
|
||
12 years ago
|
|
||
|
def do_protect(self, func, args):
|
||
12 years ago
|
if self.wallet.use_encryption:
|
||
|
password = self.password_dialog()
|
||
|
if not password:
|
||
|
return
|
||
|
else:
|
||
|
password = None
|
||
12 years ago
|
|
||
|
if args != (False,):
|
||
|
args = (self,) + args + (password,)
|
||
|
else:
|
||
|
args = (self,password)
|
||
|
apply( func, args)
|
||
|
|
||
12 years ago
|
|
||
12 years ago
|
@protected
|
||
|
def show_private_key(self, address, password):
|
||
|
if not address: return
|
||
12 years ago
|
try:
|
||
12 years ago
|
pk_list = self.wallet.get_private_key(address, password)
|
||
12 years ago
|
except BaseException, e:
|
||
|
self.show_message(str(e))
|
||
|
return
|
||
12 years ago
|
QMessageBox.information(self, _('Private key'), 'Address'+ ': ' + address + '\n\n' + _('Private key') + ': ' + '\n'.join(pk_list), _('OK'))
|
||
12 years ago
|
|
||
|
|
||
12 years ago
|
@protected
|
||
|
def do_sign(self, address, message, signature, password):
|
||
|
try:
|
||
|
sig = self.wallet.sign_message(str(address.text()), str(message.toPlainText()), password)
|
||
|
signature.setText(sig)
|
||
|
except BaseException, e:
|
||
|
self.show_message(str(e))
|
||
|
|
||
|
def sign_message(self, address):
|
||
12 years ago
|
if not address: return
|
||
|
d = QDialog(self)
|
||
|
d.setModal(1)
|
||
12 years ago
|
d.setWindowTitle(_('Sign Message'))
|
||
12 years ago
|
d.setMinimumSize(410, 290)
|
||
12 years ago
|
|
||
|
tab_widget = QTabWidget()
|
||
|
tab = QWidget()
|
||
|
layout = QGridLayout(tab)
|
||
|
|
||
|
sign_address = QLineEdit()
|
||
12 years ago
|
|
||
12 years ago
|
sign_address.setText(address)
|
||
|
layout.addWidget(QLabel(_('Address')), 1, 0)
|
||
|
layout.addWidget(sign_address, 1, 1)
|
||
|
|
||
|
sign_message = QTextEdit()
|
||
|
layout.addWidget(QLabel(_('Message')), 2, 0)
|
||
12 years ago
|
layout.addWidget(sign_message, 2, 1)
|
||
|
layout.setRowStretch(2,3)
|
||
12 years ago
|
|
||
12 years ago
|
sign_signature = QTextEdit()
|
||
12 years ago
|
layout.addWidget(QLabel(_('Signature')), 3, 0)
|
||
|
layout.addWidget(sign_signature, 3, 1)
|
||
12 years ago
|
layout.setRowStretch(3,1)
|
||
12 years ago
|
|
||
|
|
||
|
hbox = QHBoxLayout()
|
||
|
b = QPushButton(_("Sign"))
|
||
|
hbox.addWidget(b)
|
||
12 years ago
|
b.clicked.connect(lambda: self.do_sign(sign_address, sign_message, sign_signature))
|
||
12 years ago
|
b = QPushButton(_("Close"))
|
||
|
b.clicked.connect(d.accept)
|
||
|
hbox.addWidget(b)
|
||
|
layout.addLayout(hbox, 4, 1)
|
||
12 years ago
|
tab_widget.addTab(tab, _("Sign"))
|
||
12 years ago
|
|
||
|
|
||
|
tab = QWidget()
|
||
|
layout = QGridLayout(tab)
|
||
|
|
||
|
verify_address = QLineEdit()
|
||
|
layout.addWidget(QLabel(_('Address')), 1, 0)
|
||
|
layout.addWidget(verify_address, 1, 1)
|
||
|
|
||
|
verify_message = QTextEdit()
|
||
|
layout.addWidget(QLabel(_('Message')), 2, 0)
|
||
12 years ago
|
layout.addWidget(verify_message, 2, 1)
|
||
|
layout.setRowStretch(2,3)
|
||
12 years ago
|
|
||
12 years ago
|
verify_signature = QTextEdit()
|
||
12 years ago
|
layout.addWidget(QLabel(_('Signature')), 3, 0)
|
||
|
layout.addWidget(verify_signature, 3, 1)
|
||
12 years ago
|
layout.setRowStretch(3,1)
|
||
12 years ago
|
|
||
|
def do_verify():
|
||
12 years ago
|
if self.wallet.verify_message(verify_address.text(), str(verify_signature.toPlainText()), str(verify_message.toPlainText())):
|
||
12 years ago
|
self.show_message(_("Signature verified"))
|
||
12 years ago
|
else:
|
||
|
self.show_message(_("Error: wrong signature"))
|
||
12 years ago
|
|
||
|
hbox = QHBoxLayout()
|
||
|
b = QPushButton(_("Verify"))
|
||
|
b.clicked.connect(do_verify)
|
||
|
hbox.addWidget(b)
|
||
|
b = QPushButton(_("Close"))
|
||
|
b.clicked.connect(d.accept)
|
||
|
hbox.addWidget(b)
|
||
|
layout.addLayout(hbox, 4, 1)
|
||
12 years ago
|
tab_widget.addTab(tab, _("Verify"))
|
||
12 years ago
|
|
||
|
vbox = QVBoxLayout()
|
||
|
vbox.addWidget(tab_widget)
|
||
|
d.setLayout(vbox)
|
||
|
d.exec_()
|
||
|
|
||
|
|
||
12 years ago
|
|
||
13 years ago
|
|
||
13 years ago
|
def question(self, msg):
|
||
13 years ago
|
return QMessageBox.question(self, _('Message'), msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.No) == QMessageBox.Yes
|
||
13 years ago
|
|
||
13 years ago
|
def show_message(self, msg):
|
||
13 years ago
|
QMessageBox.information(self, _('Message'), msg, _('OK'))
|
||
13 years ago
|
|
||
13 years ago
|
def password_dialog(self ):
|
||
|
d = QDialog(self)
|
||
13 years ago
|
d.setModal(1)
|
||
|
|
||
13 years ago
|
pw = QLineEdit()
|
||
|
pw.setEchoMode(2)
|
||
|
|
||
13 years ago
|
vbox = QVBoxLayout()
|
||
13 years ago
|
msg = _('Please enter your password')
|
||
13 years ago
|
vbox.addWidget(QLabel(msg))
|
||
13 years ago
|
|
||
13 years ago
|
grid = QGridLayout()
|
||
|
grid.setSpacing(8)
|
||
13 years ago
|
grid.addWidget(QLabel(_('Password')), 1, 0)
|
||
13 years ago
|
grid.addWidget(pw, 1, 1)
|
||
13 years ago
|
vbox.addLayout(grid)
|
||
13 years ago
|
|
||
13 years ago
|
vbox.addLayout(ok_cancel_buttons(d))
|
||
12 years ago
|
d.setLayout(vbox)
|
||
13 years ago
|
|
||
12 years ago
|
self.run_hook('password_dialog', pw, grid, 1)
|
||
13 years ago
|
if not d.exec_(): return
|
||
13 years ago
|
return unicode(pw.text())
|
||
13 years ago
|
|
||
13 years ago
|
|
||
|
|
||
|
|
||
|
|
||
13 years ago
|
|
||
12 years ago
|
def generate_transaction_information_widget(self, tx):
|
||
12 years ago
|
tabs = QTabWidget(self)
|
||
|
|
||
|
tab1 = QWidget()
|
||
|
grid_ui = QGridLayout(tab1)
|
||
|
grid_ui.setColumnStretch(0,1)
|
||
|
tabs.addTab(tab1, _('Outputs') )
|
||
|
|
||
|
tree_widget = MyTreeWidget(self)
|
||
|
tree_widget.setColumnCount(2)
|
||
|
tree_widget.setHeaderLabels( [_('Address'), _('Amount')] )
|
||
|
tree_widget.setColumnWidth(0, 300)
|
||
|
tree_widget.setColumnWidth(1, 50)
|
||
|
|
||
12 years ago
|
for address, value in tx.outputs:
|
||
12 years ago
|
item = QTreeWidgetItem( [address, "%s" % ( self.format_amount(value))] )
|
||
12 years ago
|
tree_widget.addTopLevelItem(item)
|
||
|
|
||
|
tree_widget.setMaximumHeight(100)
|
||
|
|
||
|
grid_ui.addWidget(tree_widget)
|
||
|
|
||
|
tab2 = QWidget()
|
||
|
grid_ui = QGridLayout(tab2)
|
||
|
grid_ui.setColumnStretch(0,1)
|
||
|
tabs.addTab(tab2, _('Inputs') )
|
||
|
|
||
|
tree_widget = MyTreeWidget(self)
|
||
|
tree_widget.setColumnCount(2)
|
||
12 years ago
|
tree_widget.setHeaderLabels( [ _('Address'), _('Previous output')] )
|
||
12 years ago
|
|
||
12 years ago
|
for input_line in tx.inputs:
|
||
|
item = QTreeWidgetItem( [ str(input_line["address"]), str(input_line["prevout_hash"])] )
|
||
12 years ago
|
tree_widget.addTopLevelItem(item)
|
||
|
|
||
|
tree_widget.setMaximumHeight(100)
|
||
|
|
||
|
grid_ui.addWidget(tree_widget)
|
||
12 years ago
|
return tabs
|
||
12 years ago
|
|
||
|
|
||
12 years ago
|
def tx_dict_from_text(self, txt):
|
||
|
try:
|
||
|
tx_dict = json.loads(str(txt))
|
||
|
assert "hex" in tx_dict.keys()
|
||
|
assert "complete" in tx_dict.keys()
|
||
|
if not tx_dict["complete"]:
|
||
|
assert "input_info" in tx_dict.keys()
|
||
|
except:
|
||
12 years ago
|
QMessageBox.critical(None, "Unable to parse transaction", _("Electrum was unable to parse your transaction"))
|
||
12 years ago
|
return None
|
||
|
return tx_dict
|
||
12 years ago
|
|
||
|
|
||
12 years ago
|
def read_tx_from_file(self):
|
||
12 years ago
|
fileName = self.getOpenFileName(_("Select your transaction file"), "*.txn")
|
||
12 years ago
|
if not fileName:
|
||
|
return
|
||
|
try:
|
||
|
with open(fileName, "r") as f:
|
||
|
file_content = f.read()
|
||
|
except (ValueError, IOError, os.error), reason:
|
||
|
QMessageBox.critical(None,"Unable to read file or no transaction found", _("Electrum was unable to open your transaction file") + "\n" + str(reason))
|
||
12 years ago
|
|
||
12 years ago
|
return self.tx_dict_from_text(file_content)
|
||
12 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
@protected
|
||
12 years ago
|
def sign_raw_transaction(self, tx, input_info, dialog ="", password = ""):
|
||
12 years ago
|
try:
|
||
|
self.wallet.signrawtransaction(tx, input_info, [], password)
|
||
|
|
||
12 years ago
|
fileName = self.getSaveFileName(_("Select where to save your signed transaction"), 'signed_%s.txn' % (tx.hash()[0:8]), "*.txn")
|
||
12 years ago
|
if fileName:
|
||
|
with open(fileName, "w+") as f:
|
||
|
f.write(json.dumps(tx.as_dict(),indent=4) + '\n')
|
||
12 years ago
|
self.show_message(_("Transaction saved successfully"))
|
||
12 years ago
|
if dialog:
|
||
|
dialog.done(0)
|
||
12 years ago
|
except BaseException, e:
|
||
|
self.show_message(str(e))
|
||
12 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
def send_raw_transaction(self, raw_tx, dialog = ""):
|
||
12 years ago
|
result, result_message = self.wallet.sendtx( raw_tx )
|
||
12 years ago
|
if result:
|
||
12 years ago
|
self.show_message("Transaction successfully sent: %s" % (result_message))
|
||
12 years ago
|
if dialog:
|
||
|
dialog.done(0)
|
||
12 years ago
|
else:
|
||
|
self.show_message("There was a problem sending your transaction:\n %s" % (result_message))
|
||
12 years ago
|
|
||
12 years ago
|
def do_process_from_text(self):
|
||
12 years ago
|
text = text_dialog(self, _('Input raw transaction'), _("Transaction:"), _("Load transaction"))
|
||
|
if not text:
|
||
|
return
|
||
|
tx_dict = self.tx_dict_from_text(text)
|
||
12 years ago
|
if tx_dict:
|
||
12 years ago
|
self.create_process_transaction_window(tx_dict)
|
||
12 years ago
|
|
||
12 years ago
|
def do_process_from_file(self):
|
||
12 years ago
|
tx_dict = self.read_tx_from_file()
|
||
12 years ago
|
if tx_dict:
|
||
|
self.create_process_transaction_window(tx_dict)
|
||
12 years ago
|
|
||
12 years ago
|
def do_process_from_csvReader(self, csvReader):
|
||
|
outputs = []
|
||
|
try:
|
||
|
for row in csvReader:
|
||
|
address = row[0]
|
||
|
amount = float(row[1])
|
||
|
amount = int(100000000*amount)
|
||
|
outputs.append((address, amount))
|
||
|
except (ValueError, IOError, os.error), reason:
|
||
|
QMessageBox.critical(None,"Unable to read file or no transaction found", _("Electrum was unable to open your transaction file") + "\n" + str(reason))
|
||
|
return
|
||
|
|
||
|
try:
|
||
|
tx = self.wallet.make_unsigned_transaction(outputs, None, None, account=self.current_account)
|
||
|
except BaseException, e:
|
||
|
self.show_message(str(e))
|
||
|
return
|
||
|
|
||
|
tx_dict = tx.as_dict()
|
||
|
self.create_process_transaction_window(tx_dict)
|
||
|
|
||
|
def do_process_from_csv_file(self):
|
||
|
fileName = self.getOpenFileName(_("Select your transaction CSV"), "*.csv")
|
||
|
if not fileName:
|
||
|
return
|
||
|
try:
|
||
|
with open(fileName, "r") as f:
|
||
|
csvReader = csv.reader(f)
|
||
|
self.do_process_from_csvReader(csvReader)
|
||
|
except (ValueError, IOError, os.error), reason:
|
||
|
QMessageBox.critical(None,"Unable to read file or no transaction found", _("Electrum was unable to open your transaction file") + "\n" + str(reason))
|
||
|
return
|
||
|
|
||
|
def do_process_from_csv_text(self):
|
||
|
text = text_dialog(self, _('Input CSV'), _("CSV:"), _("Load CSV"))
|
||
|
if not text:
|
||
|
return
|
||
|
f = StringIO.StringIO(text)
|
||
|
csvReader = csv.reader(f)
|
||
|
self.do_process_from_csvReader(csvReader)
|
||
|
|
||
12 years ago
|
def create_process_transaction_window(self, tx_dict):
|
||
12 years ago
|
tx = Transaction(tx_dict["hex"])
|
||
12 years ago
|
|
||
12 years ago
|
dialog = QDialog(self)
|
||
|
dialog.setMinimumWidth(500)
|
||
12 years ago
|
dialog.setWindowTitle(_('Process raw transaction'))
|
||
12 years ago
|
dialog.setModal(1)
|
||
|
|
||
12 years ago
|
l = QGridLayout()
|
||
|
dialog.setLayout(l)
|
||
|
|
||
12 years ago
|
l.addWidget(QLabel(_("Transaction status:")), 3,0)
|
||
12 years ago
|
l.addWidget(QLabel(_("Actions")), 4,0)
|
||
12 years ago
|
|
||
12 years ago
|
if tx_dict["complete"] == False:
|
||
12 years ago
|
l.addWidget(QLabel(_("Unsigned")), 3,1)
|
||
12 years ago
|
if self.wallet.seed :
|
||
|
b = QPushButton("Sign transaction")
|
||
12 years ago
|
input_info = json.loads(tx_dict["input_info"])
|
||
|
b.clicked.connect(lambda: self.sign_raw_transaction(tx, input_info, dialog))
|
||
12 years ago
|
l.addWidget(b, 4, 1)
|
||
12 years ago
|
else:
|
||
12 years ago
|
l.addWidget(QLabel(_("Wallet is de-seeded, can't sign.")), 4,1)
|
||
12 years ago
|
else:
|
||
12 years ago
|
l.addWidget(QLabel(_("Signed")), 3,1)
|
||
12 years ago
|
b = QPushButton("Broadcast transaction")
|
||
12 years ago
|
b.clicked.connect(lambda: self.send_raw_transaction(tx, dialog))
|
||
12 years ago
|
l.addWidget(b,4,1)
|
||
12 years ago
|
|
||
12 years ago
|
l.addWidget( self.generate_transaction_information_widget(tx), 0,0,2,3)
|
||
|
cancelButton = QPushButton(_("Cancel"))
|
||
|
cancelButton.clicked.connect(lambda: dialog.done(0))
|
||
|
l.addWidget(cancelButton, 4,2)
|
||
12 years ago
|
|
||
12 years ago
|
dialog.exec_()
|
||
12 years ago
|
|
||
|
|
||
12 years ago
|
@protected
|
||
|
def do_export_privkeys(self, password):
|
||
12 years ago
|
self.show_message("%s\n%s\n%s" % (_("WARNING: ALL your private keys are secret."), _("Exposing a single private key can compromise your entire wallet!"), _("In particular, DO NOT use 'redeem private key' services proposed by third parties.")))
|
||
|
|
||
|
try:
|
||
|
select_export = _('Select file to export your private keys to')
|
||
12 years ago
|
fileName = self.getSaveFileName(select_export, 'electrum-private-keys.csv', "*.csv")
|
||
12 years ago
|
if fileName:
|
||
|
with open(fileName, "w+") as csvfile:
|
||
|
transaction = csv.writer(csvfile)
|
||
|
transaction.writerow(["address", "private_key"])
|
||
|
|
||
12 years ago
|
addresses = self.wallet.addresses(True)
|
||
12 years ago
|
|
||
12 years ago
|
for addr in addresses:
|
||
|
pk = "".join(self.wallet.get_private_key(addr, password))
|
||
12 years ago
|
transaction.writerow(["%34s"%addr,pk])
|
||
12 years ago
|
|
||
|
self.show_message(_("Private keys exported."))
|
||
|
|
||
|
except (IOError, os.error), reason:
|
||
|
export_error_label = _("Electrum was unable to produce a private key-export.")
|
||
|
QMessageBox.critical(None,"Unable to create csv", export_error_label + "\n" + str(reason))
|
||
|
|
||
|
except BaseException, e:
|
||
|
self.show_message(str(e))
|
||
|
return
|
||
|
|
||
13 years ago
|
|
||
12 years ago
|
def do_import_labels(self):
|
||
12 years ago
|
labelsFile = self.getOpenFileName(_("Open labels file"), "*.dat")
|
||
12 years ago
|
if not labelsFile: return
|
||
|
try:
|
||
|
f = open(labelsFile, 'r')
|
||
|
data = f.read()
|
||
|
f.close()
|
||
12 years ago
|
for key, value in json.loads(data).items():
|
||
12 years ago
|
self.wallet.set_label(key, value)
|
||
12 years ago
|
QMessageBox.information(None, _("Labels imported"), _("Your labels were imported from")+" '%s'" % str(labelsFile))
|
||
12 years ago
|
except (IOError, os.error), reason:
|
||
12 years ago
|
QMessageBox.critical(None, _("Unable to import labels"), _("Electrum was unable to import your labels.")+"\n" + str(reason))
|
||
12 years ago
|
|
||
12 years ago
|
|
||
|
def do_export_labels(self):
|
||
|
labels = self.wallet.labels
|
||
|
try:
|
||
12 years ago
|
fileName = self.getSaveFileName(_("Select file to save your labels"), 'electrum_labels.dat', "*.dat")
|
||
|
if fileName:
|
||
|
with open(fileName, 'w+') as f:
|
||
|
json.dump(labels, f)
|
||
|
QMessageBox.information(None, "Labels exported", _("Your labels where exported to")+" '%s'" % str(fileName))
|
||
12 years ago
|
except (IOError, os.error), reason:
|
||
12 years ago
|
QMessageBox.critical(None, "Unable to export labels", _("Electrum was unable to export your labels.")+"\n" + str(reason))
|
||
12 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
def do_export_history(self):
|
||
12 years ago
|
from lite_window import csv_transaction
|
||
12 years ago
|
csv_transaction(self.wallet)
|
||
13 years ago
|
|
||
12 years ago
|
|
||
|
@protected
|
||
|
def do_import_privkey(self, password):
|
||
12 years ago
|
if not self.wallet.imported_keys:
|
||
12 years ago
|
r = QMessageBox.question(None, _('Warning'), '<b>'+_('Warning') +':\n</b><br/>'+ _('Imported keys are not recoverable from seed.') + ' ' \
|
||
|
+ _('If you ever need to restore your wallet from its seed, these keys will be lost.') + '<p>' \
|
||
12 years ago
|
+ _('Are you sure you understand what you are doing?'), 3, 4)
|
||
|
if r == 4: return
|
||
|
|
||
12 years ago
|
text = text_dialog(self, _('Import private keys'), _("Enter private keys")+':', _("Import"))
|
||
|
if not text: return
|
||
|
|
||
|
text = str(text).split()
|
||
|
badkeys = []
|
||
|
addrlist = []
|
||
|
for key in text:
|
||
|
try:
|
||
|
addr = self.wallet.import_key(key, password)
|
||
|
except BaseException as e:
|
||
|
badkeys.append(key)
|
||
|
continue
|
||
|
if not addr:
|
||
|
badkeys.append(key)
|
||
12 years ago
|
else:
|
||
12 years ago
|
addrlist.append(addr)
|
||
|
if addrlist:
|
||
|
QMessageBox.information(self, _('Information'), _("The following addresses were added") + ':\n' + '\n'.join(addrlist))
|
||
|
if badkeys:
|
||
|
QMessageBox.critical(self, _('Error'), _("The following inputs could not be imported") + ':\n'+ '\n'.join(badkeys))
|
||
|
self.update_receive_tab()
|
||
|
self.update_history_tab()
|
||
12 years ago
|
|
||
12 years ago
|
|
||
13 years ago
|
def settings_dialog(self):
|
||
|
d = QDialog(self)
|
||
12 years ago
|
d.setWindowTitle(_('Electrum Settings'))
|
||
13 years ago
|
d.setModal(1)
|
||
13 years ago
|
vbox = QVBoxLayout()
|
||
12 years ago
|
|
||
|
tabs = QTabWidget(self)
|
||
12 years ago
|
self.settings_tab = tabs
|
||
12 years ago
|
vbox.addWidget(tabs)
|
||
|
|
||
12 years ago
|
tab1 = QWidget()
|
||
|
grid_ui = QGridLayout(tab1)
|
||
12 years ago
|
grid_ui.setColumnStretch(0,1)
|
||
12 years ago
|
tabs.addTab(tab1, _('Display') )
|
||
13 years ago
|
|
||
12 years ago
|
nz_label = QLabel(_('Display zeros'))
|
||
12 years ago
|
grid_ui.addWidget(nz_label, 0, 0)
|
||
12 years ago
|
nz_e = AmountEdit(None,True)
|
||
12 years ago
|
nz_e.setText("%d"% self.num_zeros)
|
||
12 years ago
|
grid_ui.addWidget(nz_e, 0, 1)
|
||
13 years ago
|
msg = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
|
||
12 years ago
|
grid_ui.addWidget(HelpButton(msg), 0, 2)
|
||
12 years ago
|
if not self.config.is_modifiable('num_zeros'):
|
||
|
for w in [nz_e, nz_label]: w.setEnabled(False)
|
||
12 years ago
|
|
||
12 years ago
|
lang_label=QLabel(_('Language') + ':')
|
||
12 years ago
|
grid_ui.addWidget(lang_label, 1, 0)
|
||
12 years ago
|
lang_combo = QComboBox()
|
||
12 years ago
|
from i18n import languages
|
||
12 years ago
|
lang_combo.addItems(languages.values())
|
||
12 years ago
|
try:
|
||
12 years ago
|
index = languages.keys().index(self.config.get("language",''))
|
||
12 years ago
|
except:
|
||
|
index = 0
|
||
|
lang_combo.setCurrentIndex(index)
|
||
12 years ago
|
grid_ui.addWidget(lang_combo, 1, 1)
|
||
|
grid_ui.addWidget(HelpButton(_('Select which language is used in the GUI (after restart).')+' '), 1, 2)
|
||
12 years ago
|
if not self.config.is_modifiable('language'):
|
||
|
for w in [lang_combo, lang_label]: w.setEnabled(False)
|
||
|
|
||
12 years ago
|
currencies = self.exchanger.get_currencies()
|
||
|
currencies.insert(0, "None")
|
||
12 years ago
|
|
||
12 years ago
|
cur_label=QLabel(_('Currency') + ':')
|
||
12 years ago
|
grid_ui.addWidget(cur_label , 2, 0)
|
||
12 years ago
|
cur_combo = QComboBox()
|
||
|
cur_combo.addItems(currencies)
|
||
|
try:
|
||
|
index = currencies.index(self.config.get('currency', "None"))
|
||
|
except:
|
||
|
index = 0
|
||
|
cur_combo.setCurrentIndex(index)
|
||
12 years ago
|
grid_ui.addWidget(cur_combo, 2, 1)
|
||
|
grid_ui.addWidget(HelpButton(_('Select which currency is used for quotes.')+' '), 2, 2)
|
||
12 years ago
|
|
||
12 years ago
|
expert_cb = QCheckBox(_('Expert mode'))
|
||
|
expert_cb.setChecked(self.expert_mode)
|
||
|
grid_ui.addWidget(expert_cb, 3, 0)
|
||
12 years ago
|
hh = _('In expert mode, your client will:') + '\n' \
|
||
|
+ _(' - Show change addresses in the Receive tab') + '\n' \
|
||
|
+ _(' - Display the balance of each address') + '\n' \
|
||
|
+ _(' - Add freeze/prioritize actions to addresses.')
|
||
12 years ago
|
grid_ui.addWidget(HelpButton(hh), 3, 2)
|
||
|
grid_ui.setRowStretch(4,1)
|
||
12 years ago
|
|
||
|
# wallet tab
|
||
|
tab2 = QWidget()
|
||
|
grid_wallet = QGridLayout(tab2)
|
||
|
grid_wallet.setColumnStretch(0,1)
|
||
|
tabs.addTab(tab2, _('Wallet') )
|
||
|
|
||
|
fee_label = QLabel(_('Transaction fee'))
|
||
|
grid_wallet.addWidget(fee_label, 0, 0)
|
||
12 years ago
|
fee_e = AmountEdit(self.base_unit)
|
||
12 years ago
|
fee_e.setText(self.format_amount(self.wallet.fee).strip())
|
||
12 years ago
|
grid_wallet.addWidget(fee_e, 0, 2)
|
||
12 years ago
|
msg = _('Fee per kilobyte of transaction.') + ' ' \
|
||
12 years ago
|
+ _('Recommended value') + ': ' + self.format_amount(50000)
|
||
12 years ago
|
grid_wallet.addWidget(HelpButton(msg), 0, 3)
|
||
12 years ago
|
if not self.config.is_modifiable('fee_per_kb'):
|
||
12 years ago
|
for w in [fee_e, fee_label]: w.setEnabled(False)
|
||
|
|
||
12 years ago
|
usechange_cb = QCheckBox(_('Use change addresses'))
|
||
|
usechange_cb.setChecked(self.wallet.use_change)
|
||
|
grid_wallet.addWidget(usechange_cb, 1, 0)
|
||
12 years ago
|
grid_wallet.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions.')+' '), 1, 3)
|
||
12 years ago
|
if not self.config.is_modifiable('use_change'): usechange_cb.setEnabled(False)
|
||
12 years ago
|
|
||
|
gap_label = QLabel(_('Gap limit'))
|
||
|
grid_wallet.addWidget(gap_label, 2, 0)
|
||
12 years ago
|
gap_e = AmountEdit(None,True)
|
||
12 years ago
|
gap_e.setText("%d"% self.wallet.gap_limit)
|
||
12 years ago
|
grid_wallet.addWidget(gap_e, 2, 2)
|
||
12 years ago
|
msg = _('The gap limit is the maximal number of contiguous unused addresses in your sequence of receiving addresses.') + '\n' \
|
||
|
+ _('You may increase it if you need more receiving addresses.') + '\n\n' \
|
||
|
+ _('Your current gap limit is') + ': %d'%self.wallet.gap_limit + '\n' \
|
||
12 years ago
|
+ _('Given the current status of your address sequence, the minimum gap limit you can use is:')+' ' + '%d'%self.wallet.min_acceptable_gap() + '\n\n' \
|
||
12 years ago
|
+ _('Warning') + ': ' \
|
||
|
+ _('The gap limit parameter must be provided in order to recover your wallet from seed.') + ' ' \
|
||
|
+ _('Do not modify it if you do not understand what you are doing, or if you expect to recover your wallet without knowing it!') + '\n\n'
|
||
12 years ago
|
grid_wallet.addWidget(HelpButton(msg), 2, 3)
|
||
12 years ago
|
if not self.config.is_modifiable('gap_limit'):
|
||
|
for w in [gap_e, gap_label]: w.setEnabled(False)
|
||
|
|
||
12 years ago
|
units = ['BTC', 'mBTC']
|
||
|
unit_label = QLabel(_('Base unit'))
|
||
|
grid_wallet.addWidget(unit_label, 3, 0)
|
||
|
unit_combo = QComboBox()
|
||
|
unit_combo.addItems(units)
|
||
|
unit_combo.setCurrentIndex(units.index(self.base_unit()))
|
||
|
grid_wallet.addWidget(unit_combo, 3, 2)
|
||
|
grid_wallet.addWidget(HelpButton(_('Base unit of your wallet.')\
|
||
|
+ '\n1BTC=1000mBTC.\n' \
|
||
|
+ _(' This settings affects the fields in the Send tab')+' '), 3, 3)
|
||
|
grid_wallet.setRowStretch(4,1)
|
||
12 years ago
|
|
||
12 years ago
|
# plugins
|
||
12 years ago
|
if self.plugins:
|
||
12 years ago
|
tab5 = QScrollArea()
|
||
12 years ago
|
tab5.setEnabled(True)
|
||
|
tab5.setWidgetResizable(True)
|
||
|
|
||
|
grid_plugins = QGridLayout()
|
||
12 years ago
|
grid_plugins.setColumnStretch(0,1)
|
||
12 years ago
|
|
||
12 years ago
|
w = QWidget()
|
||
12 years ago
|
w.setLayout(grid_plugins)
|
||
|
tab5.setWidget(w)
|
||
|
|
||
|
w.setMinimumHeight(len(self.plugins)*35)
|
||
|
|
||
12 years ago
|
tabs.addTab(tab5, _('Plugins') )
|
||
|
def mk_toggle(cb, p):
|
||
12 years ago
|
return lambda: cb.setChecked(p.toggle())
|
||
12 years ago
|
for i, p in enumerate(self.plugins):
|
||
|
try:
|
||
12 years ago
|
cb = QCheckBox(p.fullname())
|
||
12 years ago
|
cb.setDisabled(not p.is_available())
|
||
12 years ago
|
cb.setChecked(p.is_enabled())
|
||
12 years ago
|
cb.clicked.connect(mk_toggle(cb,p))
|
||
12 years ago
|
grid_plugins.addWidget(cb, i, 0)
|
||
12 years ago
|
if p.requires_settings():
|
||
12 years ago
|
grid_plugins.addWidget(EnterButton(_('Settings'), p.settings_dialog), i, 1)
|
||
12 years ago
|
grid_plugins.addWidget(HelpButton(p.description()), i, 2)
|
||
12 years ago
|
except:
|
||
|
print_msg("Error: cannot display plugin", p)
|
||
|
traceback.print_exc(file=sys.stdout)
|
||
|
grid_plugins.setRowStretch(i+1,1)
|
||
12 years ago
|
|
||
12 years ago
|
self.run_hook('create_settings_tab', tabs)
|
||
12 years ago
|
|
||
13 years ago
|
vbox.addLayout(ok_cancel_buttons(d))
|
||
13 years ago
|
d.setLayout(vbox)
|
||
13 years ago
|
|
||
13 years ago
|
# run the dialog
|
||
13 years ago
|
if not d.exec_(): return
|
||
|
|
||
13 years ago
|
fee = unicode(fee_e.text())
|
||
13 years ago
|
try:
|
||
12 years ago
|
fee = self.read_amount(fee)
|
||
13 years ago
|
except:
|
||
13 years ago
|
QMessageBox.warning(self, _('Error'), _('Invalid value') +': %s'%fee, _('OK'))
|
||
13 years ago
|
return
|
||
|
|
||
12 years ago
|
self.wallet.set_fee(fee)
|
||
13 years ago
|
|
||
|
nz = unicode(nz_e.text())
|
||
|
try:
|
||
|
nz = int( nz )
|
||
|
if nz>8: nz=8
|
||
|
except:
|
||
13 years ago
|
QMessageBox.warning(self, _('Error'), _('Invalid value')+':%s'%nz, _('OK'))
|
||
13 years ago
|
return
|
||
|
|
||
12 years ago
|
if self.num_zeros != nz:
|
||
|
self.num_zeros = nz
|
||
12 years ago
|
self.config.set_key('num_zeros', nz, True)
|
||
13 years ago
|
self.update_history_tab()
|
||
13 years ago
|
self.update_receive_tab()
|
||
13 years ago
|
|
||
12 years ago
|
usechange_result = usechange_cb.isChecked()
|
||
12 years ago
|
if self.wallet.use_change != usechange_result:
|
||
|
self.wallet.use_change = usechange_result
|
||
12 years ago
|
self.config.set_key('use_change', self.wallet.use_change, True)
|
||
|
|
||
12 years ago
|
unit_result = units[unit_combo.currentIndex()]
|
||
|
if self.base_unit() != unit_result:
|
||
|
self.decimal_point = 8 if unit_result == 'BTC' else 5
|
||
|
self.config.set_key('decimal_point', self.decimal_point, True)
|
||
|
self.update_history_tab()
|
||
|
self.update_status()
|
||
|
|
||
12 years ago
|
try:
|
||
|
n = int(gap_e.text())
|
||
|
except:
|
||
|
QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
|
||
|
return
|
||
12 years ago
|
|
||
12 years ago
|
if self.wallet.gap_limit != n:
|
||
|
r = self.wallet.change_gap_limit(n)
|
||
|
if r:
|
||
|
self.update_receive_tab()
|
||
12 years ago
|
self.config.set_key('gap_limit', self.wallet.gap_limit, True)
|
||
12 years ago
|
else:
|
||
13 years ago
|
QMessageBox.warning(self, _('Error'), _('Invalid value'), _('OK'))
|
||
12 years ago
|
|
||
|
need_restart = False
|
||
|
|
||
|
lang_request = languages.keys()[lang_combo.currentIndex()]
|
||
|
if lang_request != self.config.get('language'):
|
||
|
self.config.set_key("language", lang_request, True)
|
||
|
need_restart = True
|
||
12 years ago
|
|
||
|
cur_request = str(currencies[cur_combo.currentIndex()])
|
||
|
if cur_request != self.config.get('currency', "None"):
|
||
|
self.config.set_key('currency', cur_request, True)
|
||
|
self.update_wallet()
|
||
12 years ago
|
|
||
12 years ago
|
self.run_hook('close_settings_dialog')
|
||
12 years ago
|
|
||
12 years ago
|
if need_restart:
|
||
|
QMessageBox.warning(self, _('Success'), _('Please restart Electrum to activate the new GUI settings'), _('OK'))
|
||
13 years ago
|
|
||
12 years ago
|
self.receive_tab_set_mode(expert_cb.isChecked())
|
||
13 years ago
|
|
||
12 years ago
|
def run_network_dialog(self):
|
||
12 years ago
|
NetworkDialog(self.wallet.network, self.config, self).do_exec()
|
||
13 years ago
|
|
||
13 years ago
|
def closeEvent(self, event):
|
||
|
g = self.geometry()
|
||
12 years ago
|
self.config.set_key("winpos-qt", [g.left(),g.top(),g.width(),g.height()], True)
|
||
12 years ago
|
self.save_column_widths()
|
||
12 years ago
|
self.config.set_key("console-history", self.console.history[-50:], True)
|
||
13 years ago
|
event.accept()
|
||
13 years ago
|
|