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.
2564 lines
94 KiB
2564 lines
94 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/>.
|
||
|
|
||
13 years ago
|
import sys, time, datetime, re
|
||
12 years ago
|
from i18n import _, set_language
|
||
|
from electrum.util import print_error
|
||
|
import os.path, json, ast
|
||
13 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
|
||
13 years ago
|
import PyQt4.QtGui as QtGui
|
||
12 years ago
|
from electrum.interface import DEFAULT_SERVERS
|
||
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
|
||
|
from electrum.bitcoin import Transaction, is_valid
|
||
|
from electrum import mnemonic
|
||
|
|
||
|
import bmp, pyqrnative, qrscanner
|
||
12 years ago
|
import exchange_rate
|
||
13 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'
|
||
|
|
||
13 years ago
|
ALIAS_REGEXP = '^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$'
|
||
13 years ago
|
|
||
12 years ago
|
from electrum import ELECTRUM_VERSION
|
||
12 years ago
|
import re
|
||
|
|
||
|
class UpdateLabel(QtGui.QLabel):
|
||
|
def __init__(self, config, parent=None):
|
||
|
QtGui.QLabel.__init__(self, parent)
|
||
12 years ago
|
self.new_version = False
|
||
12 years ago
|
|
||
|
try:
|
||
12 years ago
|
con = httplib.HTTPConnection('electrum.org', 80, timeout=5)
|
||
12 years ago
|
con.request("GET", "/version")
|
||
|
res = con.getresponse()
|
||
12 years ago
|
except socket.error as msg:
|
||
12 years ago
|
print_error("Could not retrieve version information")
|
||
12 years ago
|
return
|
||
|
|
||
|
if res.status == 200:
|
||
|
self.latest_version = res.read()
|
||
|
self.latest_version = self.latest_version.replace("\n","")
|
||
12 years ago
|
if(re.match('^\d+(\.\d+)*$', self.latest_version)):
|
||
12 years ago
|
self.config = config
|
||
|
self.current_version = ELECTRUM_VERSION
|
||
|
if(self.compare_versions(self.latest_version, self.current_version) == 1):
|
||
12 years ago
|
latest_seen = self.config.get("last_seen_version",ELECTRUM_VERSION)
|
||
12 years ago
|
if(self.compare_versions(self.latest_version, latest_seen) == 1):
|
||
12 years ago
|
self.new_version = True
|
||
12 years ago
|
self.setText(_("New version available") + ": " + self.latest_version)
|
||
12 years ago
|
|
||
12 years ago
|
|
||
|
def compare_versions(self, version1, version2):
|
||
12 years ago
|
def normalize(v):
|
||
|
return [int(x) for x in re.sub(r'(\.0+)*$','', v).split(".")]
|
||
|
return cmp(normalize(version1), normalize(version2))
|
||
12 years ago
|
|
||
|
def ignore_this_version(self):
|
||
|
self.setText("")
|
||
12 years ago
|
self.config.set_key("last_seen_version", self.latest_version, True)
|
||
12 years ago
|
QMessageBox.information(self, _("Preference saved"), _("Notifications about this update will not be shown again."))
|
||
|
self.dialog.done(0)
|
||
|
|
||
|
def ignore_all_version(self):
|
||
|
self.setText("")
|
||
12 years ago
|
self.config.set_key("last_seen_version", "9.9.9", True)
|
||
12 years ago
|
QMessageBox.information(self, _("Preference saved"), _("No more notifications about version updates will be shown."))
|
||
|
self.dialog.done(0)
|
||
|
|
||
|
def open_website(self):
|
||
|
webbrowser.open("http://electrum.org/download.html")
|
||
|
self.dialog.done(0)
|
||
|
|
||
|
def mouseReleaseEvent(self, event):
|
||
|
dialog = QDialog(self)
|
||
|
dialog.setWindowTitle(_('Electrum update'))
|
||
|
dialog.setModal(1)
|
||
|
|
||
|
main_layout = QGridLayout()
|
||
12 years ago
|
main_layout.addWidget(QLabel(_("A new version of Electrum is available:")+" " + self.latest_version), 0,0,1,3)
|
||
12 years ago
|
|
||
|
ignore_version = QPushButton(_("Ignore this version"))
|
||
|
ignore_version.clicked.connect(self.ignore_this_version)
|
||
|
|
||
|
ignore_all_versions = QPushButton(_("Ignore all versions"))
|
||
|
ignore_all_versions.clicked.connect(self.ignore_all_version)
|
||
|
|
||
|
open_website = QPushButton(_("Goto download page"))
|
||
|
open_website.clicked.connect(self.open_website)
|
||
|
|
||
|
main_layout.addWidget(ignore_version, 1, 0)
|
||
|
main_layout.addWidget(ignore_all_versions, 1, 1)
|
||
|
main_layout.addWidget(open_website, 1, 2)
|
||
|
|
||
|
dialog.setLayout(main_layout)
|
||
|
|
||
|
self.dialog = dialog
|
||
|
|
||
|
if not dialog.exec_(): return
|
||
|
|
||
13 years ago
|
def numbify(entry, is_int = False):
|
||
13 years ago
|
text = unicode(entry.text()).strip()
|
||
13 years ago
|
pos = entry.cursorPosition()
|
||
13 years ago
|
chars = '0123456789'
|
||
|
if not is_int: chars +='.'
|
||
|
s = ''.join([i for i in text if i in chars])
|
||
|
if not is_int:
|
||
|
if '.' in s:
|
||
|
p = s.find('.')
|
||
|
s = s.replace('.','')
|
||
|
s = s[:p] + '.' + s[p:p+8]
|
||
|
try:
|
||
|
amount = int( Decimal(s) * 100000000 )
|
||
|
except:
|
||
|
amount = None
|
||
|
else:
|
||
|
try:
|
||
|
amount = int( s )
|
||
|
except:
|
||
|
amount = None
|
||
|
entry.setText(s)
|
||
13 years ago
|
entry.setCursorPosition(pos)
|
||
13 years ago
|
return amount
|
||
|
|
||
13 years ago
|
|
||
13 years ago
|
class Timer(QtCore.QThread):
|
||
13 years ago
|
def run(self):
|
||
|
while True:
|
||
13 years ago
|
self.emit(QtCore.SIGNAL('timersignal'))
|
||
13 years ago
|
time.sleep(0.5)
|
||
|
|
||
13 years ago
|
class HelpButton(QPushButton):
|
||
|
def __init__(self, text):
|
||
|
QPushButton.__init__(self, '?')
|
||
13 years ago
|
self.setFocusPolicy(Qt.NoFocus)
|
||
13 years ago
|
self.setFixedWidth(20)
|
||
|
self.clicked.connect(lambda: QMessageBox.information(self, 'Help', text, 'OK') )
|
||
|
|
||
|
|
||
13 years ago
|
class EnterButton(QPushButton):
|
||
|
def __init__(self, text, func):
|
||
|
QPushButton.__init__(self, text)
|
||
|
self.func = func
|
||
|
self.clicked.connect(func)
|
||
|
|
||
|
def keyPressEvent(self, e):
|
||
|
if e.key() == QtCore.Qt.Key_Return:
|
||
|
apply(self.func,())
|
||
|
|
||
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
|
||
|
|
||
|
def keyPressEvent(self, e):
|
||
|
if e.key() == QtCore.Qt.Key_Return:
|
||
|
apply(self.func,())
|
||
|
|
||
13 years ago
|
|
||
13 years ago
|
class QRCodeWidget(QWidget):
|
||
|
|
||
12 years ago
|
def __init__(self, data = None, size=4):
|
||
12 years ago
|
QWidget.__init__(self)
|
||
12 years ago
|
self.setMinimumSize(210, 210)
|
||
12 years ago
|
self.addr = None
|
||
|
self.qr = None
|
||
12 years ago
|
self.size = size
|
||
12 years ago
|
if data:
|
||
|
self.set_addr(data)
|
||
|
self.update_qr()
|
||
13 years ago
|
|
||
|
def set_addr(self, addr):
|
||
12 years ago
|
if self.addr != addr:
|
||
|
self.addr = addr
|
||
|
self.qr = None
|
||
12 years ago
|
self.update()
|
||
12 years ago
|
|
||
12 years ago
|
def update_qr(self):
|
||
12 years ago
|
if self.addr and not self.qr:
|
||
12 years ago
|
self.qr = pyqrnative.QRCode(self.size, pyqrnative.QRErrorCorrectLevel.L)
|
||
12 years ago
|
self.qr.addData(self.addr)
|
||
|
self.qr.make()
|
||
12 years ago
|
self.update()
|
||
|
|
||
|
def paintEvent(self, e):
|
||
|
|
||
|
if not self.addr:
|
||
|
return
|
||
|
|
||
|
black = QColor(0, 0, 0, 255)
|
||
|
white = QColor(255, 255, 255, 255)
|
||
|
|
||
|
if not self.qr:
|
||
|
qp = QtGui.QPainter()
|
||
|
qp.begin(self)
|
||
|
qp.setBrush(white)
|
||
|
qp.setPen(white)
|
||
|
qp.drawRect(0, 0, 198, 198)
|
||
|
qp.end()
|
||
|
return
|
||
12 years ago
|
|
||
13 years ago
|
k = self.qr.getModuleCount()
|
||
12 years ago
|
qp = QtGui.QPainter()
|
||
|
qp.begin(self)
|
||
12 years ago
|
r = qp.viewport()
|
||
|
boxsize = min(r.width(), r.height())*0.8/k
|
||
|
size = k*boxsize
|
||
|
left = (r.width() - size)/2
|
||
|
top = (r.height() - size)/2
|
||
|
|
||
13 years ago
|
for r in range(k):
|
||
|
for c in range(k):
|
||
|
if self.qr.isDark(r, c):
|
||
|
qp.setBrush(black)
|
||
|
qp.setPen(black)
|
||
|
else:
|
||
|
qp.setBrush(white)
|
||
|
qp.setPen(white)
|
||
12 years ago
|
qp.drawRect(left+c*boxsize, top+r*boxsize, boxsize, boxsize)
|
||
13 years ago
|
qp.end()
|
||
|
|
||
|
|
||
12 years ago
|
|
||
|
class QR_Window(QWidget):
|
||
|
|
||
12 years ago
|
def __init__(self, exchanger):
|
||
12 years ago
|
QWidget.__init__(self)
|
||
12 years ago
|
self.exchanger = exchanger
|
||
12 years ago
|
self.setWindowTitle('Electrum - '+_('Invoice'))
|
||
12 years ago
|
self.setMinimumSize(800, 250)
|
||
|
self.address = ''
|
||
|
self.labe = ''
|
||
|
self.amount = 0
|
||
|
self.setFocusPolicy(QtCore.Qt.NoFocus)
|
||
|
|
||
|
main_box = QHBoxLayout()
|
||
|
|
||
12 years ago
|
self.qrw = QRCodeWidget()
|
||
12 years ago
|
main_box.addWidget(self.qrw, 1)
|
||
12 years ago
|
|
||
|
vbox = QVBoxLayout()
|
||
|
main_box.addLayout(vbox)
|
||
|
|
||
|
self.address_label = QLabel("")
|
||
12 years ago
|
self.address_label.setFont(QFont(MONOSPACE_FONT))
|
||
12 years ago
|
vbox.addWidget(self.address_label)
|
||
|
|
||
|
self.label_label = QLabel("")
|
||
|
vbox.addWidget(self.label_label)
|
||
|
|
||
|
self.amount_label = QLabel("")
|
||
|
vbox.addWidget(self.amount_label)
|
||
|
|
||
|
vbox.addStretch(1)
|
||
|
self.setLayout(main_box)
|
||
|
|
||
|
|
||
12 years ago
|
def set_content(self, addr, label, amount, currency):
|
||
12 years ago
|
self.address = addr
|
||
12 years ago
|
address_text = "<span style='font-size: 18pt'>%s</span>" % addr if addr else ""
|
||
12 years ago
|
self.address_label.setText(address_text)
|
||
|
|
||
12 years ago
|
if currency == 'BTC': currency = None
|
||
|
amount_text = ''
|
||
|
if amount:
|
||
|
if currency:
|
||
|
self.amount = Decimal(amount) / self.exchanger.exchange(1, currency) if currency else amount
|
||
|
else:
|
||
|
self.amount = Decimal(amount)
|
||
|
self.amount = self.amount.quantize(Decimal('1.0000'))
|
||
|
|
||
|
if currency:
|
||
|
amount_text += "<span style='font-size: 18pt'>%s %s</span><br/>" % (amount, currency)
|
||
|
amount_text += "<span style='font-size: 21pt'>%s</span> <span style='font-size: 16pt'>BTC</span> " % str(self.amount)
|
||
12 years ago
|
self.amount_label.setText(amount_text)
|
||
|
|
||
|
self.label = label
|
||
12 years ago
|
label_text = "<span style='font-size: 21pt'>%s</span>" % label if label else ""
|
||
12 years ago
|
self.label_label.setText(label_text)
|
||
|
|
||
|
msg = 'bitcoin:'+self.address
|
||
|
if self.amount is not None:
|
||
12 years ago
|
msg += '?amount=%s'%(str( self.amount))
|
||
12 years ago
|
if self.label is not None:
|
||
|
msg += '&label=%s'%(self.label)
|
||
|
elif self.label is not None:
|
||
|
msg += '?label=%s'%(self.label)
|
||
12 years ago
|
|
||
|
self.qrw.set_addr( msg )
|
||
|
|
||
|
|
||
|
|
||
|
|
||
12 years ago
|
def waiting_dialog(f):
|
||
|
|
||
|
s = Timer()
|
||
|
s.start()
|
||
|
w = QDialog()
|
||
|
w.resize(200, 70)
|
||
|
w.setWindowTitle('Electrum')
|
||
|
l = QLabel('')
|
||
|
vbox = QVBoxLayout()
|
||
|
vbox.addWidget(l)
|
||
|
w.setLayout(vbox)
|
||
|
w.show()
|
||
|
def ff():
|
||
|
s = f()
|
||
|
if s: l.setText(s)
|
||
|
else: w.close()
|
||
|
w.connect(s, QtCore.SIGNAL('timersignal'), ff)
|
||
|
w.exec_()
|
||
|
w.destroy()
|
||
|
|
||
13 years ago
|
|
||
13 years ago
|
def ok_cancel_buttons(dialog):
|
||
|
hbox = QHBoxLayout()
|
||
|
hbox.addStretch(1)
|
||
|
b = QPushButton("OK")
|
||
|
hbox.addWidget(b)
|
||
|
b.clicked.connect(dialog.accept)
|
||
|
b = QPushButton("Cancel")
|
||
|
hbox.addWidget(b)
|
||
|
b.clicked.connect(dialog.reject)
|
||
|
return hbox
|
||
|
|
||
|
|
||
12 years ago
|
default_column_widths = { "history":[40,140,350,140], "contacts":[350,330],
|
||
12 years ago
|
"receive":[[370],[370,200,130,130],[370,200,130,130]] }
|
||
12 years ago
|
|
||
13 years ago
|
class ElectrumWindow(QMainWindow):
|
||
13 years ago
|
|
||
12 years ago
|
def __init__(self, wallet, config):
|
||
13 years ago
|
QMainWindow.__init__(self)
|
||
12 years ago
|
self.lite = None
|
||
13 years ago
|
self.wallet = wallet
|
||
12 years ago
|
self.config = config
|
||
12 years ago
|
self.wallet.interface.register_callback('updated', self.update_callback)
|
||
12 years ago
|
self.wallet.interface.register_callback('banner', lambda: self.emit(QtCore.SIGNAL('banner_signal')) )
|
||
12 years ago
|
self.wallet.interface.register_callback('disconnected', self.update_callback)
|
||
12 years ago
|
self.wallet.interface.register_callback('disconnecting', self.update_callback)
|
||
13 years ago
|
|
||
12 years ago
|
self.receive_tab_mode = config.get('qt_receive_tab_mode', 0)
|
||
12 years ago
|
self.merchant_name = config.get('merchant_name', 'Invoice')
|
||
12 years ago
|
|
||
12 years ago
|
set_language(config.get('language'))
|
||
|
|
||
12 years ago
|
self.qr_window = None
|
||
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)
|
||
13 years ago
|
self.create_status_bar()
|
||
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])
|
||
12 years ago
|
title = 'Electrum ' + self.wallet.electrum_version + ' - ' + self.config.path
|
||
12 years ago
|
if not self.wallet.seed: title += ' [%s]' % (_('seedless'))
|
||
13 years ago
|
self.setWindowTitle( title )
|
||
13 years ago
|
|
||
13 years ago
|
QShortcut(QKeySequence("Ctrl+W"), self, self.close)
|
||
|
QShortcut(QKeySequence("Ctrl+Q"), self, self.close)
|
||
13 years ago
|
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() ))
|
||
13 years ago
|
|
||
|
self.connect(self, QtCore.SIGNAL('updatesignal'), self.update_wallet)
|
||
12 years ago
|
self.connect(self, QtCore.SIGNAL('banner_signal'), lambda: self.console.showMessage(self.wallet.banner) )
|
||
13 years ago
|
self.history_list.setFocus(True)
|
||
12 years ago
|
|
||
|
self.exchanger = exchange_rate.Exchanger(self)
|
||
12 years ago
|
self.toggle_QR_window(self.receive_tab_mode == 2)
|
||
12 years ago
|
self.connect(self, SIGNAL("refresh_balance()"), self.update_wallet)
|
||
13 years ago
|
|
||
13 years ago
|
# dark magic fix by flatfly; https://bitcointalk.org/index.php?topic=73651.msg959913#msg959913
|
||
|
if platform.system() == 'Windows':
|
||
13 years ago
|
n = 3 if self.wallet.seed else 2
|
||
13 years ago
|
tabs.setCurrentIndex (n)
|
||
13 years ago
|
tabs.setCurrentIndex (0)
|
||
|
|
||
12 years ago
|
# set initial message
|
||
|
self.console.showMessage(self.wallet.banner)
|
||
|
|
||
12 years ago
|
def close(self):
|
||
|
QMainWindow.close(self)
|
||
|
if self.qr_window:
|
||
|
self.qr_window.close()
|
||
|
self.qr_window = None
|
||
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):
|
||
|
if self.qr_window:
|
||
12 years ago
|
self.qr_window.qrw.update_qr()
|
||
12 years ago
|
|
||
13 years ago
|
if self.payto_e.hasFocus():
|
||
|
return
|
||
13 years ago
|
r = unicode( self.payto_e.text() )
|
||
13 years ago
|
if r != self.previous_payto_e:
|
||
|
self.previous_payto_e = r
|
||
|
r = r.strip()
|
||
|
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
|
||
|
try:
|
||
13 years ago
|
to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
|
||
13 years ago
|
except:
|
||
|
return
|
||
|
if to_address:
|
||
13 years ago
|
s = r + ' <' + to_address + '>'
|
||
13 years ago
|
self.payto_e.setText(s)
|
||
13 years ago
|
|
||
13 years ago
|
|
||
13 years ago
|
def update_callback(self):
|
||
|
self.emit(QtCore.SIGNAL('updatesignal'))
|
||
|
|
||
13 years ago
|
def update_wallet(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:
|
||
|
c, u = self.wallet.get_balance()
|
||
13 years ago
|
text = _( "Balance" ) + ": %s "%( format_satoshis(c,False,self.wallet.num_zeros) )
|
||
13 years ago
|
if u: text += "[%s unconfirmed]"%( format_satoshis(u,True,self.wallet.num_zeros).strip() )
|
||
12 years ago
|
text += self.create_quote_text(Decimal(c+u)/100000000)
|
||
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.status_text = text
|
||
13 years ago
|
self.statusBar().showMessage(text)
|
||
13 years ago
|
self.status_button.setIcon( icon )
|
||
13 years ago
|
|
||
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(None)
|
||
|
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():
|
||
|
is_mine, v, fee = self.wallet.get_tx_value(tx)
|
||
|
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:
|
||
|
if fee:
|
||
|
vbox.addWidget(QLabel("Amount sent: %s"% format_satoshis(v-fee, False)))
|
||
|
vbox.addWidget(QLabel("Transaction fee: %s"% format_satoshis(fee, False)))
|
||
|
else:
|
||
|
vbox.addWidget(QLabel("Amount sent: %s"% format_satoshis(v, False)))
|
||
|
vbox.addWidget(QLabel("Transaction fee: unknown"))
|
||
|
else:
|
||
|
vbox.addWidget(QLabel("Amount received: %s"% format_satoshis(v, False)))
|
||
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
|
s = self.wallet.labels.get(tx_hash)
|
||
13 years ago
|
text = unicode( item.text(2) )
|
||
13 years ago
|
if text:
|
||
13 years ago
|
self.wallet.labels[tx_hash] = text
|
||
|
item.setForeground(2, QBrush(QColor('black')))
|
||
|
else:
|
||
|
if s: self.wallet.labels.pop(tx_hash)
|
||
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
|
||
|
c = 2 if is_recv else 1
|
||
|
item = l.currentItem()
|
||
|
item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
|
||
|
l.editItem( item, c )
|
||
|
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
|
||
|
|
||
12 years ago
|
def edit_amount(self):
|
||
|
l = self.receive_list
|
||
|
item = l.currentItem()
|
||
|
item.setFlags(Qt.ItemIsEditable|Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled | Qt.ItemIsDragEnabled)
|
||
|
l.editItem( item, 3 )
|
||
|
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():
|
||
13 years ago
|
addr = unicode( item.text(column_addr) )
|
||
13 years ago
|
label = unicode( item.text(column_label) )
|
||
|
if label in self.wallet.aliases.keys():
|
||
13 years ago
|
return
|
||
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) )
|
||
|
changed = False
|
||
|
|
||
|
if text:
|
||
|
if text not in self.wallet.aliases.keys():
|
||
|
old_addr = self.wallet.labels.get(text)
|
||
|
if old_addr != addr:
|
||
|
self.wallet.labels[addr] = text
|
||
|
changed = True
|
||
|
else:
|
||
|
print_error("Error: This is one of your aliases")
|
||
|
label = self.wallet.labels.get(addr,'')
|
||
|
item.setText(column_label, QString(label))
|
||
13 years ago
|
else:
|
||
12 years ago
|
s = self.wallet.labels.get(addr)
|
||
|
if s:
|
||
|
self.wallet.labels.pop(addr)
|
||
|
changed = True
|
||
12 years ago
|
|
||
12 years ago
|
if changed:
|
||
|
self.update_history_tab()
|
||
|
self.update_completions()
|
||
|
|
||
|
self.recv_changed(item)
|
||
|
|
||
12 years ago
|
if column == 2:
|
||
12 years ago
|
address = str( item.text(column_addr) )
|
||
|
text = str( item.text(3) )
|
||
12 years ago
|
try:
|
||
|
index = self.wallet.addresses.index(address)
|
||
|
except:
|
||
|
return
|
||
|
|
||
12 years ago
|
text = text.strip().upper()
|
||
|
m = re.match('^(\d+(|\.\d*))\s*(|BTC|EUR|USD|GBP|CNY|JPY|RUB|BRL)$', text)
|
||
|
if m:
|
||
|
amount = m.group(1)
|
||
|
currency = m.group(3)
|
||
|
if not currency:
|
||
|
currency = 'BTC'
|
||
12 years ago
|
else:
|
||
12 years ago
|
currency = currency.upper()
|
||
|
self.wallet.requested_amounts[address] = (amount, currency)
|
||
12 years ago
|
|
||
12 years ago
|
label = self.wallet.labels.get(address)
|
||
|
if label is None:
|
||
|
label = self.merchant_name + ' - %04d'%(index+1)
|
||
|
self.wallet.labels[address] = label
|
||
12 years ago
|
|
||
12 years ago
|
if self.qr_window:
|
||
|
self.qr_window.set_content( address, label, amount, currency )
|
||
12 years ago
|
|
||
12 years ago
|
else:
|
||
|
item.setText(3,'')
|
||
|
if address in self.wallet.requested_amounts:
|
||
|
self.wallet.requested_amounts.pop(address)
|
||
|
|
||
12 years ago
|
self.update_receive_item(self.receive_list.currentItem())
|
||
|
|
||
12 years ago
|
|
||
|
def recv_changed(self, a):
|
||
|
"current item changed"
|
||
12 years ago
|
if a is not None and self.qr_window and self.qr_window.isVisible():
|
||
12 years ago
|
address = str(a.text(1))
|
||
|
label = self.wallet.labels.get(address)
|
||
12 years ago
|
try:
|
||
|
amount, currency = self.wallet.requested_amounts.get(address, (None, None))
|
||
|
except:
|
||
|
amount, currency = None, None
|
||
|
self.qr_window.set_content( address, label, amount, currency )
|
||
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():
|
||
|
tx_hash, conf, is_mine, value, fee, balance, timestamp = item
|
||
12 years ago
|
if conf:
|
||
12 years ago
|
try:
|
||
12 years ago
|
time_str = datetime.datetime.fromtimestamp( timestamp).isoformat(' ')[:-3]
|
||
12 years ago
|
except:
|
||
|
time_str = "unknown"
|
||
|
if conf == -1:
|
||
|
icon = None
|
||
12 years ago
|
if conf == 0:
|
||
|
icon = QIcon(":icons/unconfirmed.png")
|
||
|
elif conf < 6:
|
||
12 years ago
|
icon = QIcon(":icons/clock%d.png"%conf)
|
||
|
else:
|
||
|
icon = QIcon(":icons/confirmed.png")
|
||
13 years ago
|
else:
|
||
|
time_str = 'pending'
|
||
13 years ago
|
icon = QIcon(":icons/unconfirmed.png")
|
||
13 years ago
|
|
||
12 years ago
|
if value is not None:
|
||
|
v_str = format_satoshis(value, True, self.wallet.num_zeros)
|
||
|
else:
|
||
|
v_str = '--'
|
||
|
|
||
|
balance_str = format_satoshis(balance, False, self.wallet.num_zeros)
|
||
|
|
||
|
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
|
|
||
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)
|
||
13 years ago
|
|
||
|
def fill_from_qr():
|
||
|
qrcode = qrscanner.scan_qr()
|
||
|
if 'address' in qrcode:
|
||
|
self.payto_e.setText(qrcode['address'])
|
||
|
if 'amount' in qrcode:
|
||
|
self.amount_e.setText(str(qrcode['amount']))
|
||
|
if 'label' in qrcode:
|
||
|
self.message_e.setText(qrcode['label'])
|
||
|
if 'message' in qrcode:
|
||
|
self.message_e.setText("%s (%s)" % (self.message_e.text(), qrcode['message']))
|
||
|
|
||
|
|
||
|
if qrscanner.is_available():
|
||
|
b = QPushButton(_("Scan QR code"))
|
||
|
b.clicked.connect(fill_from_qr)
|
||
|
grid.addWidget(b, 1, 5)
|
||
|
|
||
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
|
|
||
13 years ago
|
self.amount_e = QLineEdit()
|
||
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' \
|
||
|
+ _('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.')), 3, 3)
|
||
13 years ago
|
|
||
13 years ago
|
self.fee_e = QLineEdit()
|
||
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
|
b = ''
|
||
|
if self.wallet.seed:
|
||
|
b = EnterButton(_("Send"), self.do_send)
|
||
|
else:
|
||
|
b = EnterButton(_("Create unsigned transaction"), self.do_send)
|
||
13 years ago
|
grid.addWidget(b, 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
|
||
|
amount = numbify(self.amount_e)
|
||
|
fee = numbify(self.fee_e)
|
||
|
if not is_fee: fee = None
|
||
|
if amount is None:
|
||
|
return
|
||
|
inputs, total, fee = self.wallet.choose_tx_inputs( amount, fee )
|
||
|
if not is_fee:
|
||
|
self.fee_e.setText( str( Decimal( fee ) / 100000000 ) )
|
||
|
if inputs:
|
||
|
palette = QPalette()
|
||
|
palette.setColor(self.amount_e.foregroundRole(), QColor('black'))
|
||
12 years ago
|
text = self.status_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" )
|
||
|
|
||
|
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) )
|
||
|
|
||
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 + '>')
|
||
|
l = l + self.wallet.aliases.keys()
|
||
|
|
||
|
self.completions.setStringList(l)
|
||
|
|
||
|
|
||
|
|
||
13 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
|
# alias
|
||
|
m1 = re.match(ALIAS_REGEXP, r)
|
||
|
# label or alias, with address in brackets
|
||
|
m2 = re.match('(.*?)\s*\<([1-9A-HJ-NP-Za-km-z]{26,})\>', r)
|
||
13 years ago
|
|
||
|
if m1:
|
||
13 years ago
|
to_address = self.wallet.get_alias(r, True, self.show_message, self.question)
|
||
13 years ago
|
if not to_address:
|
||
|
return
|
||
|
elif m2:
|
||
13 years ago
|
to_address = m2.group(2)
|
||
13 years ago
|
else:
|
||
|
to_address = r
|
||
|
|
||
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:
|
||
13 years ago
|
amount = int( Decimal( unicode( self.amount_e.text())) * 100000000 )
|
||
13 years ago
|
except:
|
||
13 years ago
|
QMessageBox.warning(self, _('Error'), _('Invalid Amount'), _('OK'))
|
||
13 years ago
|
return
|
||
|
try:
|
||
13 years ago
|
fee = int( Decimal( unicode( self.fee_e.text())) * 100000000 )
|
||
13 years ago
|
except:
|
||
13 years ago
|
QMessageBox.warning(self, _('Error'), _('Invalid Fee'), _('OK'))
|
||
13 years ago
|
return
|
||
|
|
||
|
if self.wallet.use_encryption:
|
||
|
password = self.password_dialog()
|
||
|
if not password:
|
||
|
return
|
||
|
else:
|
||
|
password = None
|
||
|
|
||
|
try:
|
||
12 years ago
|
tx = self.wallet.mktx( [(to_address, amount)], password, fee)
|
||
13 years ago
|
except BaseException, e:
|
||
13 years ago
|
self.show_message(str(e))
|
||
13 years ago
|
return
|
||
12 years ago
|
|
||
12 years ago
|
|
||
|
for cb in self.wallet.plugin_hooks.get('send_tx'):
|
||
|
apply(cb, (wallet, self, tx))
|
||
|
|
||
|
|
||
12 years ago
|
if label:
|
||
|
self.wallet.labels[tx.hash()] = label
|
||
|
|
||
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 = 'unsigned_tx_%s' % (time.mktime(time.gmtime()))
|
||
|
try:
|
||
|
fileName = QFileDialog.getSaveFileName(QWidget(), _("Select a transaction filename"), os.path.expanduser('~/%s' % (filename)))
|
||
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
|
|
||
|
|
||
13 years ago
|
def set_url(self, url):
|
||
13 years ago
|
payto, amount, label, message, signature, identity, url = self.wallet.parse_url(url, self.show_message, self.question)
|
||
13 years ago
|
self.tabs.setCurrentIndex(1)
|
||
13 years ago
|
label = self.wallet.labels.get(payto)
|
||
13 years ago
|
m_addr = label + ' <'+ payto+'>' if label else payto
|
||
|
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)
|
||
|
|
||
|
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'), _('Requested'), _('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.recv_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.receive_tab_mode = i
|
||
|
self.config.set_key('qt_receive_tab_mode', self.receive_tab_mode, True)
|
||
12 years ago
|
self.wallet.save()
|
||
|
self.update_receive_tab()
|
||
12 years ago
|
self.toggle_QR_window(self.receive_tab_mode == 2)
|
||
12 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
def save_column_widths(self):
|
||
12 years ago
|
if self.receive_tab_mode == 0:
|
||
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.receive_tab_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
|
|
||
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.addWidget(EnterButton(_("New"), self.new_contact_dialog))
|
||
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.imported_keys.pop(addr)
|
||
|
self.update_receive_tab()
|
||
|
self.update_history_tab()
|
||
|
self.wallet.save()
|
||
|
|
||
|
|
||
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))
|
||
13 years ago
|
menu = QMenu()
|
||
12 years ago
|
menu.addAction(_("Copy to clipboard"), lambda: self.app.clipboard().setText(addr))
|
||
12 years ago
|
if self.receive_tab_mode == 2:
|
||
12 years ago
|
menu.addAction(_("Request amount"), lambda: self.edit_amount())
|
||
12 years ago
|
menu.addAction(_("QR code"), lambda: ElectrumWindow.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.view_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.receive_tab_mode == 1:
|
||
|
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))
|
||
|
|
||
13 years ago
|
menu.exec_(self.receive_list.viewport().mapToGlobal(position))
|
||
|
|
||
|
|
||
13 years ago
|
def payto(self, x, is_alias):
|
||
|
if not x: return
|
||
|
if is_alias:
|
||
|
label = x
|
||
13 years ago
|
m_addr = label
|
||
13 years ago
|
else:
|
||
|
addr = x
|
||
|
label = self.wallet.labels.get(addr)
|
||
13 years ago
|
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()
|
||
|
|
||
13 years ago
|
def delete_contact(self, x, is_alias):
|
||
12 years ago
|
if self.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")):
|
||
13 years ago
|
if not is_alias and x in self.wallet.addressbook:
|
||
|
self.wallet.addressbook.remove(x)
|
||
|
if x in self.wallet.labels.keys():
|
||
|
self.wallet.labels.pop(x)
|
||
|
elif is_alias and x in self.wallet.aliases:
|
||
|
self.wallet.aliases.pop(x)
|
||
13 years ago
|
self.update_history_tab()
|
||
|
self.update_contacts_tab()
|
||
|
self.update_completions()
|
||
13 years ago
|
|
||
|
def create_contact_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.contacts_list.selectedIndexes()
|
||
|
|
||
|
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))
|
||
|
is_alias = label in self.wallet.aliases.keys()
|
||
|
x = label if is_alias else addr
|
||
13 years ago
|
menu = QMenu()
|
||
|
menu.addAction(_("Copy to Clipboard"), lambda: self.app.clipboard().setText(addr))
|
||
13 years ago
|
menu.addAction(_("Pay to"), lambda: self.payto(x, is_alias))
|
||
12 years ago
|
menu.addAction(_("QR code"), lambda: self.show_qrcode("bitcoin:" + addr, _("Address")))
|
||
13 years ago
|
if not is_alias:
|
||
13 years ago
|
menu.addAction(_("Edit label"), lambda: self.edit_label(False))
|
||
13 years ago
|
else:
|
||
13 years ago
|
menu.addAction(_("View alias details"), lambda: self.show_contact_details(label))
|
||
13 years ago
|
menu.addAction(_("Delete"), lambda: self.delete_contact(x,is_alias))
|
||
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
|
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
|
|
||
12 years ago
|
try:
|
||
|
amount, currency = self.wallet.requested_amounts.get(address, (None, None))
|
||
|
except:
|
||
|
amount, currency = None, None
|
||
|
|
||
|
amount_str = amount + (' ' + currency if currency else '') if amount is not None else ''
|
||
12 years ago
|
item.setData(2,0,amount_str)
|
||
12 years ago
|
|
||
12 years ago
|
c, u = self.wallet.get_addr_balance(address)
|
||
|
balance = format_satoshis( c + u, False, self.wallet.num_zeros )
|
||
12 years ago
|
item.setData(3,0,balance)
|
||
12 years ago
|
|
||
12 years ago
|
if self.receive_tab_mode == 1:
|
||
|
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.receive_tab_mode == 2)
|
||
|
l.setColumnHidden(3, self.receive_tab_mode == 0)
|
||
|
l.setColumnHidden(4, not self.receive_tab_mode == 1)
|
||
|
if self.receive_tab_mode == 0:
|
||
12 years ago
|
width = self.column_widths['receive'][0][0]
|
||
12 years ago
|
l.setColumnWidth(0, width)
|
||
12 years ago
|
else:
|
||
|
for i,width in enumerate(self.column_widths['receive'][self.receive_tab_mode]):
|
||
|
l.setColumnWidth(i, width)
|
||
13 years ago
|
|
||
13 years ago
|
|
||
12 years ago
|
for k, account in self.wallet.accounts.items():
|
||
|
name = account.get('name',str(k))
|
||
12 years ago
|
c,u = self.wallet.get_account_balance(k)
|
||
|
account_item = QTreeWidgetItem( [ name, '', '', format_satoshis(c+u), ''] )
|
||
12 years ago
|
l.addTopLevelItem(account_item)
|
||
|
account_item.setExpanded(True)
|
||
12 years ago
|
|
||
13 years ago
|
|
||
12 years ago
|
for is_change in [0,1]:
|
||
|
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)
|
||
|
is_red = False
|
||
|
gap = 0
|
||
13 years ago
|
|
||
12 years ago
|
for address in account[is_change]:
|
||
|
h = self.wallet.history.get(address,[])
|
||
|
|
||
|
if not is_change:
|
||
|
if h == []:
|
||
|
gap += 1
|
||
|
if gap > self.wallet.gap_limit:
|
||
|
is_red = True
|
||
|
else:
|
||
|
gap = 0
|
||
|
|
||
|
num_tx = '*' if h == ['*'] else "%d"%len(h)
|
||
|
item = QTreeWidgetItem( [ address, '', '', '', num_tx] )
|
||
|
item.setFont(0, QFont(MONOSPACE_FONT))
|
||
|
item.setFont(2, QFont(MONOSPACE_FONT))
|
||
|
self.update_receive_item(item)
|
||
12 years ago
|
if is_red:
|
||
12 years ago
|
item.setBackgroundColor(1, QColor('red'))
|
||
|
seq_item.addChild(item)
|
||
|
|
||
|
if self.wallet.imported_keys:
|
||
12 years ago
|
c,u = self.wallet.get_imported_balance()
|
||
|
account_item = QTreeWidgetItem( [ _('Imported'), '', '', format_satoshis(c+u), ''] )
|
||
12 years ago
|
l.addTopLevelItem(account_item)
|
||
|
account_item.setExpanded(True)
|
||
|
for address in self.wallet.imported_keys.keys():
|
||
|
item = QTreeWidgetItem( [ address, '', '', '', ''] )
|
||
|
item.setFont(0, QFont(MONOSPACE_FONT))
|
||
|
item.setFont(2, QFont(MONOSPACE_FONT))
|
||
|
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
|
def show_contact_details(self, m):
|
||
13 years ago
|
a = self.wallet.aliases.get(m)
|
||
|
if a:
|
||
|
if a[0] in self.wallet.authorities.keys():
|
||
|
s = self.wallet.authorities.get(a[0])
|
||
|
else:
|
||
|
s = "self-signed"
|
||
12 years ago
|
msg = _('Alias:')+' '+ m + '\n'+_('Target address:')+' '+ a[1] + '\n\n'+_('Signed by:')+' ' + s + '\n'+_('Signing address:')+' ' + a[0]
|
||
13 years ago
|
QMessageBox.information(self, 'Alias', msg, 'OK')
|
||
|
|
||
13 years ago
|
def update_contacts_tab(self):
|
||
13 years ago
|
|
||
13 years ago
|
l = self.contacts_list
|
||
|
l.clear()
|
||
13 years ago
|
|
||
13 years ago
|
alias_targets = []
|
||
13 years ago
|
for alias, v in self.wallet.aliases.items():
|
||
|
s, target = v
|
||
13 years ago
|
alias_targets.append(target)
|
||
13 years ago
|
item = QTreeWidgetItem( [ target, alias, '-'] )
|
||
13 years ago
|
item.setBackgroundColor(0, QColor('lightgray'))
|
||
13 years ago
|
l.addTopLevelItem(item)
|
||
13 years ago
|
|
||
|
for address in self.wallet.addressbook:
|
||
13 years ago
|
if address in alias_targets: continue
|
||
13 years ago
|
label = self.wallet.labels.get(address,'')
|
||
|
n = 0
|
||
12 years ago
|
for tx in self.wallet.transactions.values():
|
||
|
if address in map(lambda x: x[0], tx.outputs): n += 1
|
||
|
|
||
|
item = QTreeWidgetItem( [ address, label, "%d"%n] )
|
||
13 years ago
|
item.setFont(0, QFont(MONOSPACE_FONT))
|
||
13 years ago
|
l.addTopLevelItem(item)
|
||
13 years ago
|
|
||
13 years ago
|
l.setCurrentItem(l.topLevelItem(0))
|
||
13 years ago
|
|
||
12 years ago
|
|
||
12 years ago
|
def create_console_tab(self):
|
||
12 years ago
|
from qt_console import Console
|
||
12 years ago
|
from electrum import util, bitcoin, commands
|
||
12 years ago
|
self.console = console = Console()
|
||
12 years ago
|
self.console.history = self.config.get("console-history",[])
|
||
|
self.console.history_index = len(self.console.history)
|
||
12 years ago
|
|
||
|
#init plugins
|
||
|
for p in self.wallet.plugins:
|
||
|
try:
|
||
|
p.init_console(self.console, self)
|
||
|
except:
|
||
|
import traceback
|
||
|
print_msg("Error:cannot initialize plugin",p)
|
||
|
traceback.print_exc(file=sys.stdout)
|
||
|
|
||
12 years ago
|
|
||
12 years ago
|
console.updateNamespace({'wallet' : self.wallet, 'interface' : self.wallet.interface, '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
|
return console
|
||
13 years ago
|
|
||
12 years ago
|
|
||
13 years ago
|
def create_status_bar(self):
|
||
12 years ago
|
self.status_text = ""
|
||
13 years ago
|
sb = QStatusBar()
|
||
13 years ago
|
sb.setFixedHeight(35)
|
||
12 years ago
|
qtVersion = qVersion()
|
||
12 years ago
|
|
||
|
update_notification = UpdateLabel(self.config)
|
||
12 years ago
|
if(update_notification.new_version):
|
||
12 years ago
|
sb.addPermanentWidget(update_notification)
|
||
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 ) )
|
||
13 years ago
|
if self.wallet.seed:
|
||
12 years ago
|
sb.addPermanentWidget( StatusBarButton( QIcon(":icons/lock.png"), _("Password"), lambda: self.change_password_dialog(self.wallet, self) ) )
|
||
|
sb.addPermanentWidget( StatusBarButton( QIcon(":icons/preferences.png"), _("Preferences"), self.settings_dialog ) )
|
||
13 years ago
|
if self.wallet.seed:
|
||
12 years ago
|
sb.addPermanentWidget( StatusBarButton( QIcon(":icons/seed.png"), _("Seed"), lambda: self.show_seed_dialog(self.wallet, self) ) )
|
||
|
self.status_button = StatusBarButton( QIcon(":icons/status_disconnected.png"), _("Network"), lambda: self.network_dialog(self.wallet, self) )
|
||
13 years ago
|
sb.addPermanentWidget( self.status_button )
|
||
12 years ago
|
|
||
13 years ago
|
self.setStatusBar(sb)
|
||
12 years ago
|
|
||
|
def go_lite(self):
|
||
|
import gui_lite
|
||
12 years ago
|
self.config.set_key('gui', 'lite', True)
|
||
12 years ago
|
self.hide()
|
||
|
if self.lite:
|
||
|
self.lite.mini.show()
|
||
|
else:
|
||
|
self.lite = gui_lite.ElectrumGui(self.wallet, self.config, self)
|
||
|
self.lite.main(None)
|
||
13 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):
|
||
13 years ago
|
self.wallet.addressbook.append(address)
|
||
|
self.wallet.save()
|
||
|
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
|
def show_master_public_key(self):
|
||
|
dialog = QDialog(None)
|
||
|
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(), 6)
|
||
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
|
@classmethod
|
||
|
def show_seed_dialog(self, wallet, parent=None):
|
||
13 years ago
|
if not wallet.seed:
|
||
12 years ago
|
QMessageBox.information(parent, _('Message'), _('No seed'), _('OK'))
|
||
13 years ago
|
return
|
||
|
|
||
13 years ago
|
if wallet.use_encryption:
|
||
13 years ago
|
password = parent.password_dialog()
|
||
13 years ago
|
if not password:
|
||
|
return
|
||
13 years ago
|
else:
|
||
|
password = None
|
||
|
|
||
|
try:
|
||
12 years ago
|
seed = wallet.decode_seed(password)
|
||
13 years ago
|
except:
|
||
12 years ago
|
QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
|
||
13 years ago
|
return
|
||
13 years ago
|
|
||
12 years ago
|
self.show_seed(seed)
|
||
|
|
||
|
@classmethod
|
||
|
def show_seed(self, seed):
|
||
13 years ago
|
dialog = QDialog(None)
|
||
|
dialog.setModal(1)
|
||
12 years ago
|
dialog.setWindowTitle('Electrum' + ' - ' + _('Seed'))
|
||
13 years ago
|
|
||
13 years ago
|
brainwallet = ' '.join(mnemonic.mn_encode(seed))
|
||
13 years ago
|
|
||
12 years ago
|
label1 = QLabel(_("Your wallet generation seed is")+ ":")
|
||
12 years ago
|
|
||
12 years ago
|
seed_text = QTextEdit(brainwallet)
|
||
|
seed_text.setReadOnly(True)
|
||
12 years ago
|
seed_text.setMaximumHeight(130)
|
||
12 years ago
|
|
||
|
msg2 = _("Please write down or memorize these 12 words (order is important).") + " " \
|
||
12 years ago
|
+ _("This seed will allow you to recover your wallet in case of computer failure.") + " " \
|
||
12 years ago
|
+ _("Your seed is also displayed as QR code, in case you want to transfer it to a mobile phone.") + "<p>" \
|
||
12 years ago
|
+ "<b>"+_("WARNING")+":</b> " + _("Never disclose your seed. Never type it on a website.") + "</b><p>"
|
||
12 years ago
|
label2 = QLabel(msg2)
|
||
|
label2.setWordWrap(True)
|
||
13 years ago
|
|
||
13 years ago
|
logo = QLabel()
|
||
|
logo.setPixmap(QPixmap(":icons/seed.png").scaledToWidth(56))
|
||
12 years ago
|
logo.setMaximumWidth(60)
|
||
13 years ago
|
|
||
12 years ago
|
qrw = QRCodeWidget(seed, 4)
|
||
13 years ago
|
|
||
|
ok_button = QPushButton(_("OK"))
|
||
12 years ago
|
ok_button.setDefault(True)
|
||
13 years ago
|
ok_button.clicked.connect(dialog.accept)
|
||
|
|
||
12 years ago
|
grid = QGridLayout()
|
||
|
#main_layout.addWidget(logo, 0, 0)
|
||
|
|
||
|
grid.addWidget(logo, 0, 0)
|
||
|
grid.addWidget(label1, 0, 1)
|
||
|
|
||
|
grid.addWidget(seed_text, 1, 0, 1, 2)
|
||
|
|
||
|
grid.addWidget(qrw, 0, 2, 2, 1)
|
||
|
|
||
|
vbox = QVBoxLayout()
|
||
|
vbox.addLayout(grid)
|
||
|
vbox.addWidget(label2)
|
||
12 years ago
|
|
||
12 years ago
|
hbox = QHBoxLayout()
|
||
|
hbox.addStretch(1)
|
||
|
hbox.addWidget(ok_button)
|
||
|
vbox.addLayout(hbox)
|
||
13 years ago
|
|
||
12 years ago
|
dialog.setLayout(vbox)
|
||
13 years ago
|
dialog.exec_()
|
||
13 years ago
|
|
||
13 years ago
|
@staticmethod
|
||
12 years ago
|
def show_qrcode(data, title = "QR code"):
|
||
12 years ago
|
if not data: return
|
||
13 years ago
|
d = QDialog(None)
|
||
|
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 view_private_key(self,address):
|
||
|
if not address: return
|
||
|
if self.wallet.use_encryption:
|
||
|
password = self.password_dialog()
|
||
|
if not password:
|
||
|
return
|
||
|
else:
|
||
|
password = None
|
||
|
|
||
|
try:
|
||
12 years ago
|
pk = self.wallet.get_private_key(address, password)
|
||
12 years ago
|
except BaseException, e:
|
||
|
self.show_message(str(e))
|
||
|
return
|
||
|
|
||
|
QMessageBox.information(self, _('Private key'), 'Address'+ ': ' + address + '\n\n' + _('Private key') + ': ' + pk, _('OK'))
|
||
|
|
||
|
|
||
12 years ago
|
def sign_message(self,address):
|
||
|
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
|
|
||
|
def do_sign():
|
||
|
if self.wallet.use_encryption:
|
||
|
password = self.password_dialog()
|
||
|
if not password:
|
||
|
return
|
||
|
else:
|
||
|
password = None
|
||
|
|
||
|
try:
|
||
12 years ago
|
signature = self.wallet.sign_message(str(sign_address.text()), str(sign_message.toPlainText()), password)
|
||
12 years ago
|
sign_signature.setText(signature)
|
||
|
except BaseException, e:
|
||
|
self.show_message(str(e))
|
||
|
return
|
||
|
|
||
|
hbox = QHBoxLayout()
|
||
|
b = QPushButton(_("Sign"))
|
||
|
hbox.addWidget(b)
|
||
|
b.clicked.connect(do_sign)
|
||
|
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():
|
||
|
try:
|
||
12 years ago
|
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
|
except BaseException, e:
|
||
|
self.show_message(str(e))
|
||
|
return
|
||
|
|
||
|
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
|
def toggle_QR_window(self, show):
|
||
|
if show and not self.qr_window:
|
||
12 years ago
|
self.qr_window = QR_Window(self.exchanger)
|
||
12 years ago
|
self.qr_window.setVisible(True)
|
||
|
self.qr_window_geometry = self.qr_window.geometry()
|
||
12 years ago
|
item = self.receive_list.currentItem()
|
||
|
if item:
|
||
|
address = str(item.text(1))
|
||
|
label = self.wallet.labels.get(address)
|
||
12 years ago
|
amount, currency = self.wallet.requested_amounts.get(address, (None, None))
|
||
|
self.qr_window.set_content( address, label, amount, currency )
|
||
12 years ago
|
|
||
12 years ago
|
elif show and self.qr_window and not self.qr_window.isVisible():
|
||
|
self.qr_window.setVisible(True)
|
||
|
self.qr_window.setGeometry(self.qr_window_geometry)
|
||
|
|
||
|
elif not show and self.qr_window and self.qr_window.isVisible():
|
||
|
self.qr_window_geometry = self.qr_window.geometry()
|
||
|
self.qr_window.setVisible(False)
|
||
|
|
||
|
#self.print_button.setHidden(self.qr_window is None or not self.qr_window.isVisible())
|
||
12 years ago
|
self.receive_list.setColumnHidden(2, self.qr_window is None or not self.qr_window.isVisible())
|
||
|
self.receive_list.setColumnWidth(1, 200)
|
||
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))
|
||
13 years ago
|
d.setLayout(vbox)
|
||
13 years ago
|
|
||
|
if not d.exec_(): return
|
||
13 years ago
|
return unicode(pw.text())
|
||
13 years ago
|
|
||
13 years ago
|
|
||
|
|
||
|
|
||
|
|
||
13 years ago
|
@staticmethod
|
||
|
def change_password_dialog( wallet, parent=None ):
|
||
13 years ago
|
|
||
|
if not wallet.seed:
|
||
13 years ago
|
QMessageBox.information(parent, _('Error'), _('No seed'), _('OK'))
|
||
13 years ago
|
return
|
||
|
|
||
13 years ago
|
d = QDialog(parent)
|
||
13 years ago
|
d.setModal(1)
|
||
13 years ago
|
|
||
|
pw = QLineEdit()
|
||
|
pw.setEchoMode(2)
|
||
|
new_pw = QLineEdit()
|
||
|
new_pw.setEchoMode(2)
|
||
|
conf_pw = QLineEdit()
|
||
|
conf_pw.setEchoMode(2)
|
||
|
|
||
13 years ago
|
vbox = QVBoxLayout()
|
||
13 years ago
|
if parent:
|
||
12 years ago
|
msg = (_('Your wallet is encrypted. Use this dialog to change your password.')+'\n'\
|
||
|
+_('To disable wallet encryption, enter an empty new password.')) \
|
||
|
if wallet.use_encryption else _('Your wallet keys are not encrypted')
|
||
13 years ago
|
else:
|
||
12 years ago
|
msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\
|
||
|
+_("Leave these fields empty if you want to disable encryption.")
|
||
13 years ago
|
vbox.addWidget(QLabel(msg))
|
||
|
|
||
13 years ago
|
grid = QGridLayout()
|
||
|
grid.setSpacing(8)
|
||
|
|
||
13 years ago
|
if wallet.use_encryption:
|
||
13 years ago
|
grid.addWidget(QLabel(_('Password')), 1, 0)
|
||
13 years ago
|
grid.addWidget(pw, 1, 1)
|
||
13 years ago
|
|
||
13 years ago
|
grid.addWidget(QLabel(_('New Password')), 2, 0)
|
||
13 years ago
|
grid.addWidget(new_pw, 2, 1)
|
||
|
|
||
13 years ago
|
grid.addWidget(QLabel(_('Confirm Password')), 3, 0)
|
||
13 years ago
|
grid.addWidget(conf_pw, 3, 1)
|
||
13 years ago
|
vbox.addLayout(grid)
|
||
13 years ago
|
|
||
13 years ago
|
vbox.addLayout(ok_cancel_buttons(d))
|
||
13 years ago
|
d.setLayout(vbox)
|
||
13 years ago
|
|
||
|
if not d.exec_(): return
|
||
|
|
||
13 years ago
|
password = unicode(pw.text()) if wallet.use_encryption else None
|
||
|
new_password = unicode(new_pw.text())
|
||
|
new_password2 = unicode(conf_pw.text())
|
||
13 years ago
|
|
||
|
try:
|
||
12 years ago
|
seed = wallet.decode_seed(password)
|
||
13 years ago
|
except:
|
||
13 years ago
|
QMessageBox.warning(parent, _('Error'), _('Incorrect Password'), _('OK'))
|
||
13 years ago
|
return
|
||
|
|
||
|
if new_password != new_password2:
|
||
13 years ago
|
QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
|
||
12 years ago
|
return ElectrumWindow.change_password_dialog(wallet, parent) # Retry
|
||
13 years ago
|
|
||
13 years ago
|
wallet.update_password(seed, password, new_password)
|
||
13 years ago
|
|
||
|
@staticmethod
|
||
|
def seed_dialog(wallet, parent=None):
|
||
|
d = QDialog(parent)
|
||
|
d.setModal(1)
|
||
|
|
||
|
vbox = QVBoxLayout()
|
||
13 years ago
|
msg = _("Please enter your wallet seed or the corresponding mnemonic list of words, and the gap limit of your wallet.")
|
||
13 years ago
|
vbox.addWidget(QLabel(msg))
|
||
|
|
||
|
grid = QGridLayout()
|
||
|
grid.setSpacing(8)
|
||
|
|
||
|
seed_e = QLineEdit()
|
||
13 years ago
|
grid.addWidget(QLabel(_('Seed or mnemonic')), 1, 0)
|
||
13 years ago
|
grid.addWidget(seed_e, 1, 1)
|
||
|
|
||
|
gap_e = QLineEdit()
|
||
|
gap_e.setText("5")
|
||
13 years ago
|
grid.addWidget(QLabel(_('Gap limit')), 2, 0)
|
||
13 years ago
|
grid.addWidget(gap_e, 2, 1)
|
||
13 years ago
|
gap_e.textChanged.connect(lambda: numbify(gap_e,True))
|
||
13 years ago
|
vbox.addLayout(grid)
|
||
|
|
||
|
vbox.addLayout(ok_cancel_buttons(d))
|
||
|
d.setLayout(vbox)
|
||
|
|
||
|
if not d.exec_(): return
|
||
|
|
||
|
try:
|
||
13 years ago
|
gap = int(unicode(gap_e.text()))
|
||
13 years ago
|
except:
|
||
13 years ago
|
QMessageBox.warning(None, _('Error'), 'error', 'OK')
|
||
12 years ago
|
return
|
||
13 years ago
|
|
||
|
try:
|
||
12 years ago
|
seed = str(seed_e.text())
|
||
13 years ago
|
seed.decode('hex')
|
||
|
except:
|
||
13 years ago
|
print_error("Warning: Not hex, trying decode")
|
||
13 years ago
|
try:
|
||
|
seed = mnemonic.mn_decode( seed.split(' ') )
|
||
|
except:
|
||
13 years ago
|
QMessageBox.warning(None, _('Error'), _('I cannot decode this'), _('OK'))
|
||
12 years ago
|
return
|
||
|
|
||
13 years ago
|
if not seed:
|
||
12 years ago
|
QMessageBox.warning(None, _('Error'), _('No seed'), _('OK'))
|
||
12 years ago
|
return
|
||
|
|
||
|
return seed, gap
|
||
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 output in tx.d["outputs"]:
|
||
12 years ago
|
item = QTreeWidgetItem( ["%s" %(output["address"]), "%s" % ( format_satoshis(output["value"]))] )
|
||
|
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:
|
||
|
QMessageBox.critical(None, "Unable to parse transaction", _("Electrum was unable to parse your transaction:"))
|
||
|
return None
|
||
|
return tx_dict
|
||
12 years ago
|
|
||
|
|
||
12 years ago
|
def read_tx_from_file(self):
|
||
|
fileName = QFileDialog.getOpenFileName(QWidget(), _("Select your transaction file"), os.path.expanduser('~'))
|
||
|
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
|
|
||
|
def sign_raw_transaction(self, tx, input_info):
|
||
|
if self.wallet.use_encryption:
|
||
|
password = self.password_dialog()
|
||
|
if not password:
|
||
|
return
|
||
|
else:
|
||
|
password = None
|
||
|
|
||
|
try:
|
||
|
self.wallet.signrawtransaction(tx, input_info, [], password)
|
||
|
|
||
|
fileName = QFileDialog.getSaveFileName(QWidget(), _("Select where to save your signed transaction"), os.path.expanduser('~/signed_tx_%s' % (tx.hash()[0:8])))
|
||
|
if fileName:
|
||
|
with open(fileName, "w+") as f:
|
||
|
f.write(json.dumps(tx.as_dict(),indent=4) + '\n')
|
||
|
self.show_message(_("Transaction saved succesfully"))
|
||
|
except BaseException, e:
|
||
|
self.show_message(str(e))
|
||
|
|
||
|
|
||
|
def create_sign_transaction_window(self, tx_dict):
|
||
|
tx = Transaction(tx_dict["hex"])
|
||
12 years ago
|
|
||
|
dialog = QDialog(self)
|
||
|
dialog.setMinimumWidth(500)
|
||
|
dialog.setWindowTitle(_('Sign unsigned transaction'))
|
||
|
dialog.setModal(1)
|
||
|
|
||
12 years ago
|
vbox = QVBoxLayout()
|
||
12 years ago
|
dialog.setLayout(vbox)
|
||
12 years ago
|
vbox.addWidget( self.generate_transaction_information_widget(tx) )
|
||
12 years ago
|
|
||
12 years ago
|
if tx_dict["complete"] == True:
|
||
12 years ago
|
vbox.addWidget(QLabel(_("This transaction is already signed.")))
|
||
|
else:
|
||
12 years ago
|
vbox.addWidget(QLabel(_("Create a signed transaction.")))
|
||
|
vbox.addLayout(ok_cancel_buttons(dialog))
|
||
|
input_info = json.loads(tx_dict["input_info"])
|
||
12 years ago
|
|
||
12 years ago
|
if dialog.exec_():
|
||
|
self.sign_raw_transaction(tx, input_info)
|
||
12 years ago
|
|
||
|
|
||
|
|
||
12 years ago
|
def do_sign_from_text(self):
|
||
|
txt, ok = QInputDialog.getText(QTextEdit(), _('Sign raw transaction'), _('Transaction data in JSON') + ':')
|
||
|
if not ok:
|
||
|
return
|
||
|
tx_dict = self.tx_dict_from_text(unicode(txt))
|
||
|
if tx_dict:
|
||
|
self.create_sign_transaction_window(tx_dict)
|
||
12 years ago
|
|
||
|
|
||
12 years ago
|
def do_sign_from_file(self):
|
||
|
tx_dict = self.read_tx_from_file()
|
||
|
if tx_dict:
|
||
|
self.create_sign_transaction_window(tx_dict)
|
||
12 years ago
|
|
||
12 years ago
|
|
||
|
def send_raw_transaction(self, raw_tx):
|
||
12 years ago
|
result, result_message = self.wallet.sendtx( raw_tx )
|
||
12 years ago
|
if result:
|
||
|
self.show_message("Transaction succesfully sent: %s" % (result_message))
|
||
|
else:
|
||
|
self.show_message("There was a problem sending your transaction:\n %s" % (result_message))
|
||
|
|
||
12 years ago
|
|
||
|
def create_send_transaction_window(self, tx_dict):
|
||
|
tx = Transaction(tx_dict["hex"])
|
||
12 years ago
|
|
||
|
dialog = QDialog(self)
|
||
|
dialog.setMinimumWidth(500)
|
||
|
dialog.setWindowTitle(_('Send raw transaction'))
|
||
|
dialog.setModal(1)
|
||
|
|
||
12 years ago
|
vbox = QVBoxLayout()
|
||
12 years ago
|
dialog.setLayout(vbox)
|
||
12 years ago
|
vbox.addWidget( self.generate_transaction_information_widget(tx))
|
||
12 years ago
|
|
||
12 years ago
|
if tx_dict["complete"] == False:
|
||
12 years ago
|
vbox.addWidget(QLabel(_("This transaction is not signed yet.")))
|
||
|
else:
|
||
12 years ago
|
vbox.addWidget(QLabel(_("Broadcast this transaction")))
|
||
|
vbox.addLayout(ok_cancel_buttons(dialog))
|
||
12 years ago
|
|
||
12 years ago
|
if dialog.exec_():
|
||
|
self.send_raw_transaction(tx_dict["hex"])
|
||
12 years ago
|
|
||
|
|
||
12 years ago
|
def do_send_from_file(self):
|
||
|
tx_dict = self.read_tx_from_file()
|
||
|
if tx_dict:
|
||
|
self.create_send_transaction_window(tx_dict)
|
||
|
|
||
12 years ago
|
|
||
|
def do_send_from_text(self):
|
||
12 years ago
|
txt, ok = QInputDialog.getText(QTextEdit(), _('Send raw transaction'), _('Transaction data in JSON') + ':')
|
||
|
if not ok:
|
||
|
return
|
||
|
tx_dict = self.tx_dict_from_text(unicode(txt))
|
||
|
if tx_dict:
|
||
|
self.create_send_transaction_window(tx_dict)
|
||
12 years ago
|
|
||
|
|
||
12 years ago
|
def do_export_privkeys(self):
|
||
|
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.")))
|
||
|
|
||
|
if self.wallet.use_encryption:
|
||
|
password = self.password_dialog()
|
||
|
if not password:
|
||
|
return
|
||
|
else:
|
||
|
password = None
|
||
|
try:
|
||
|
select_export = _('Select file to export your private keys to')
|
||
|
fileName = QFileDialog.getSaveFileName(QWidget(), select_export, os.path.expanduser('~/electrum-private-keys.csv'), "*.csv")
|
||
|
if fileName:
|
||
|
with open(fileName, "w+") as csvfile:
|
||
|
transaction = csv.writer(csvfile)
|
||
|
transaction.writerow(["address", "private_key"])
|
||
|
|
||
12 years ago
|
|
||
12 years ago
|
for addr, pk in self.wallet.get_private_keys(self.wallet.addresses(True), password).items():
|
||
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 = QFileDialog.getOpenFileName(QWidget(), _("Open text file"), util.user_dir(), self.tr("Text Files (labels.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():
|
||
|
self.wallet.labels[key] = value
|
||
12 years ago
|
self.wallet.save()
|
||
12 years ago
|
QMessageBox.information(None, _("Labels imported"), _("Your labels where 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:
|
||
|
labelsFile = util.user_dir() + '/labels.dat'
|
||
|
f = open(labelsFile, 'w+')
|
||
|
json.dump(labels, f)
|
||
|
f.close()
|
||
12 years ago
|
QMessageBox.information(None, "Labels exported", _("Your labels where exported to")+" '%s'" % str(labelsFile))
|
||
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
|
|
||
|
def do_export_history(self):
|
||
|
from gui_lite import csv_transaction
|
||
|
csv_transaction(self.wallet)
|
||
13 years ago
|
|
||
12 years ago
|
def do_import_privkey(self):
|
||
12 years ago
|
if not self.wallet.imported_keys:
|
||
|
r = QMessageBox.question(None, _('Warning'), _('Warning: Imported keys are not recoverable from seed.') + ' ' \
|
||
|
+ _('If you ever need to restore your wallet from its seed, these keys will be lost.') + '\n\n' \
|
||
|
+ _('Are you sure you understand what you are doing?'), 3, 4)
|
||
|
if r == 4: return
|
||
|
|
||
|
text, ok = QInputDialog.getText(self, _('Import private key'), _('Private Key') + ':')
|
||
12 years ago
|
if not ok: return
|
||
12 years ago
|
sec = str(text).strip()
|
||
12 years ago
|
if self.wallet.use_encryption:
|
||
|
password = self.password_dialog()
|
||
|
if not password:
|
||
|
return
|
||
|
else:
|
||
|
password = None
|
||
12 years ago
|
try:
|
||
|
addr = self.wallet.import_key(sec, password)
|
||
|
if not addr:
|
||
12 years ago
|
QMessageBox.critical(None, _("Unable to import key"), "error")
|
||
12 years ago
|
else:
|
||
12 years ago
|
QMessageBox.information(None, _("Key imported"), addr)
|
||
12 years ago
|
self.update_receive_tab()
|
||
|
self.update_history_tab()
|
||
12 years ago
|
except BaseException as e:
|
||
12 years ago
|
QMessageBox.critical(None, _("Unable to import key"), str(e))
|
||
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)
|
||
|
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, 3, 0)
|
||
13 years ago
|
nz_e = QLineEdit()
|
||
|
nz_e.setText("%d"% self.wallet.num_zeros)
|
||
12 years ago
|
grid_ui.addWidget(nz_e, 3, 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), 3, 2)
|
||
13 years ago
|
nz_e.textChanged.connect(lambda: numbify(nz_e,True))
|
||
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 , 8, 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, 8, 1)
|
||
12 years ago
|
grid_ui.addWidget(HelpButton(_('Select which language is used in the GUI (after restart).')+' '), 8, 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') + ':')
|
||
|
grid_ui.addWidget(cur_label , 9, 0)
|
||
|
cur_combo = QComboBox()
|
||
|
cur_combo.addItems(currencies)
|
||
|
try:
|
||
|
index = currencies.index(self.config.get('currency', "None"))
|
||
|
except:
|
||
|
index = 0
|
||
|
cur_combo.setCurrentIndex(index)
|
||
|
grid_ui.addWidget(cur_combo, 9, 1)
|
||
12 years ago
|
grid_ui.addWidget(HelpButton(_('Select which currency is used for quotes.')+' '), 9, 2)
|
||
12 years ago
|
|
||
12 years ago
|
view_label=QLabel(_('Receive Tab') + ':')
|
||
12 years ago
|
grid_ui.addWidget(view_label , 10, 0)
|
||
12 years ago
|
view_combo = QComboBox()
|
||
12 years ago
|
view_combo.addItems([_('Simple'), _('Advanced'), _('Point of Sale')])
|
||
12 years ago
|
view_combo.setCurrentIndex(self.receive_tab_mode)
|
||
12 years ago
|
grid_ui.addWidget(view_combo, 10, 1)
|
||
12 years ago
|
hh = _('This selects the interaction mode of the "Receive" tab.')+' ' + '\n\n' \
|
||
12 years ago
|
+ _('Simple') + ': ' + _('Show only addresses and labels.') + '\n\n' \
|
||
|
+ _('Advanced') + ': ' + _('Show address balances and add extra menu items to freeze/prioritize addresses.') + '\n\n' \
|
||
|
+ _('Point of Sale') + ': ' + _('Show QR code window and amounts requested for each address. Add menu item to request amount.') + '\n\n'
|
||
|
|
||
12 years ago
|
grid_ui.addWidget(HelpButton(hh), 10, 2)
|
||
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)
|
||
|
fee_e = QLineEdit()
|
||
|
fee_e.setText("%s"% str( Decimal( self.wallet.fee)/100000000 ) )
|
||
|
grid_wallet.addWidget(fee_e, 0, 1)
|
||
|
msg = _('Fee per transaction input. Transactions involving multiple inputs tend to require a higher fee.') + ' ' \
|
||
|
+ _('Recommended value') + ': 0.001'
|
||
|
grid_wallet.addWidget(HelpButton(msg), 0, 2)
|
||
|
fee_e.textChanged.connect(lambda: numbify(fee_e,False))
|
||
|
if not self.config.is_modifiable('fee'):
|
||
|
for w in [fee_e, fee_label]: w.setEnabled(False)
|
||
|
|
||
|
usechange_label = QLabel(_('Use change addresses'))
|
||
|
grid_wallet.addWidget(usechange_label, 1, 0)
|
||
|
usechange_combo = QComboBox()
|
||
12 years ago
|
usechange_combo.addItems([_('Yes'), _('No')])
|
||
12 years ago
|
usechange_combo.setCurrentIndex(not self.wallet.use_change)
|
||
|
grid_wallet.addWidget(usechange_combo, 1, 1)
|
||
12 years ago
|
grid_wallet.addWidget(HelpButton(_('Using change addresses makes it more difficult for other people to track your transactions.')+' '), 1, 2)
|
||
12 years ago
|
if not self.config.is_modifiable('use_change'): usechange_combo.setEnabled(False)
|
||
|
|
||
|
gap_label = QLabel(_('Gap limit'))
|
||
|
grid_wallet.addWidget(gap_label, 2, 0)
|
||
|
gap_e = QLineEdit()
|
||
|
gap_e.setText("%d"% self.wallet.gap_limit)
|
||
|
grid_wallet.addWidget(gap_e, 2, 1)
|
||
|
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'
|
||
|
grid_wallet.addWidget(HelpButton(msg), 2, 2)
|
||
|
gap_e.textChanged.connect(lambda: numbify(nz_e,True))
|
||
|
if not self.config.is_modifiable('gap_limit'):
|
||
|
for w in [gap_e, gap_label]: w.setEnabled(False)
|
||
|
|
||
|
grid_wallet.setRowStretch(3,1)
|
||
|
|
||
|
|
||
12 years ago
|
# import/export tab
|
||
12 years ago
|
tab3 = QWidget()
|
||
|
grid_io = QGridLayout(tab3)
|
||
|
grid_io.setColumnStretch(0,1)
|
||
|
tabs.addTab(tab3, _('Import/Export') )
|
||
12 years ago
|
|
||
12 years ago
|
grid_io.addWidget(QLabel(_('Labels')), 1, 0)
|
||
|
grid_io.addWidget(EnterButton(_("Export"), self.do_export_labels), 1, 1)
|
||
|
grid_io.addWidget(EnterButton(_("Import"), self.do_import_labels), 1, 2)
|
||
12 years ago
|
grid_io.addWidget(HelpButton(_('Export your labels as json')), 1, 3)
|
||
12 years ago
|
|
||
|
grid_io.addWidget(QLabel(_('History')), 2, 0)
|
||
|
grid_io.addWidget(EnterButton(_("Export"), self.do_export_history), 2, 1)
|
||
12 years ago
|
grid_io.addWidget(HelpButton(_('Export your transaction history as csv')), 2, 3)
|
||
12 years ago
|
|
||
12 years ago
|
grid_io.addWidget(QLabel(_('Private keys')), 3, 0)
|
||
12 years ago
|
|
||
|
grid_io.addWidget(EnterButton(_("Export"), self.do_export_privkeys), 3, 1)
|
||
12 years ago
|
grid_io.addWidget(EnterButton(_("Import"), self.do_import_privkey), 3, 2)
|
||
12 years ago
|
grid_io.addWidget(HelpButton(_('Import private key')), 3, 3)
|
||
12 years ago
|
|
||
12 years ago
|
grid_io.addWidget(QLabel(_('Master Public Key')), 4, 0)
|
||
12 years ago
|
grid_io.addWidget(EnterButton(_("Show"), self.show_master_public_key), 4, 1)
|
||
12 years ago
|
grid_io.addWidget(HelpButton(_('Your Master Public Key can be used to create receiving addresses, but not to sign transactions.') + ' ' \
|
||
12 years ago
|
+ _('If you give it to someone, they will be able to see your transactions, but not to spend your money.') + ' ' \
|
||
|
+ _('If you restore your wallet from it, a watching-only (deseeded) wallet will be created.')), 4, 3)
|
||
12 years ago
|
|
||
12 years ago
|
grid_io.setRowStretch(4,1)
|
||
12 years ago
|
|
||
|
tab4 = QWidget()
|
||
|
grid_raw = QGridLayout(tab4)
|
||
|
grid_raw.setColumnStretch(0,1)
|
||
|
tabs.addTab(tab4, _('Raw transactions') )
|
||
|
#
|
||
|
#grid_raw.addWidget(QLabel(_("Read raw transaction")), 3, 0)
|
||
|
#grid_raw.addWidget(EnterButton(_("From file"), self.do_sign_from_file),3,1)
|
||
|
#grid_raw.addWidget(EnterButton(_("From text"), self.do_sign_from_text),3,2)
|
||
|
#grid_raw.addWidget(HelpButton(_("This will show you some useful information about an unsigned transaction")),3,3)
|
||
|
|
||
12 years ago
|
if self.wallet.seed:
|
||
|
grid_raw.addWidget(QLabel(_("Sign transaction")), 1, 0)
|
||
|
grid_raw.addWidget(EnterButton(_("From file"), self.do_sign_from_file),1,1)
|
||
|
grid_raw.addWidget(EnterButton(_("From text"), self.do_sign_from_text),1,2)
|
||
|
grid_raw.addWidget(HelpButton(_("Sign an unsigned transaction generated by a watching-only wallet")),1,3)
|
||
|
|
||
|
grid_raw.addWidget(QLabel(_("Send signed transaction")), 2, 0)
|
||
12 years ago
|
grid_raw.addWidget(EnterButton(_("From file"), self.do_send_from_file),2,1)
|
||
|
grid_raw.addWidget(EnterButton(_("From text"), self.do_send_from_text),2,2)
|
||
12 years ago
|
grid_raw.addWidget(HelpButton(_("This will broadcast a transaction to the network.")),2,3)
|
||
12 years ago
|
grid_raw.setRowStretch(3,1)
|
||
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:
|
||
|
fee = int( 100000000 * Decimal(fee) )
|
||
|
except:
|
||
13 years ago
|
QMessageBox.warning(self, _('Error'), _('Invalid value') +': %s'%fee, _('OK'))
|
||
13 years ago
|
return
|
||
|
|
||
13 years ago
|
if self.wallet.fee != fee:
|
||
|
self.wallet.fee = fee
|
||
|
self.wallet.save()
|
||
|
|
||
|
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
|
||
|
|
||
|
if self.wallet.num_zeros != nz:
|
||
|
self.wallet.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_combo.currentIndex() == 0
|
||
|
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
|
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
|
|
||
|
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(view_combo.currentIndex())
|
||
13 years ago
|
|
||
|
|
||
13 years ago
|
@staticmethod
|
||
|
def network_dialog(wallet, parent=None):
|
||
13 years ago
|
interface = wallet.interface
|
||
13 years ago
|
if parent:
|
||
13 years ago
|
if interface.is_connected:
|
||
12 years ago
|
status = _("Connected to")+" %s\n%d "%(interface.host, wallet.verifier.height)+_("blocks")
|
||
13 years ago
|
else:
|
||
13 years ago
|
status = _("Not connected")
|
||
12 years ago
|
server = interface.server
|
||
13 years ago
|
else:
|
||
|
import random
|
||
12 years ago
|
status = _("Please choose a server.") + "\n" + _("Select 'Cancel' if you are offline.")
|
||
12 years ago
|
server = interface.server
|
||
12 years ago
|
|
||
12 years ago
|
plist, servers_list = interface.get_servers_list()
|
||
13 years ago
|
|
||
13 years ago
|
d = QDialog(parent)
|
||
13 years ago
|
d.setModal(1)
|
||
13 years ago
|
d.setWindowTitle(_('Server'))
|
||
13 years ago
|
d.setMinimumSize(375, 20)
|
||
13 years ago
|
|
||
13 years ago
|
vbox = QVBoxLayout()
|
||
12 years ago
|
vbox.setSpacing(30)
|
||
13 years ago
|
|
||
13 years ago
|
hbox = QHBoxLayout()
|
||
|
l = QLabel()
|
||
|
l.setPixmap(QPixmap(":icons/network.png"))
|
||
12 years ago
|
hbox.addStretch(10)
|
||
12 years ago
|
hbox.addWidget(l)
|
||
13 years ago
|
hbox.addWidget(QLabel(status))
|
||
12 years ago
|
hbox.addStretch(50)
|
||
13 years ago
|
vbox.addLayout(hbox)
|
||
|
|
||
13 years ago
|
|
||
12 years ago
|
# grid layout
|
||
|
grid = QGridLayout()
|
||
|
grid.setSpacing(8)
|
||
|
vbox.addLayout(grid)
|
||
13 years ago
|
|
||
12 years ago
|
# server
|
||
|
server_protocol = QComboBox()
|
||
|
server_host = QLineEdit()
|
||
|
server_host.setFixedWidth(200)
|
||
|
server_port = QLineEdit()
|
||
|
server_port.setFixedWidth(60)
|
||
12 years ago
|
|
||
|
protocol_names = ['TCP', 'HTTP', 'TCP/SSL', 'HTTPS']
|
||
|
protocol_letters = 'thsg'
|
||
12 years ago
|
DEFAULT_PORTS = {'t':'50001', 's':'50002', 'h':'8081', 'g':'8082'}
|
||
12 years ago
|
server_protocol.addItems(protocol_names)
|
||
12 years ago
|
|
||
|
grid.addWidget(QLabel(_('Server') + ':'), 0, 0)
|
||
|
grid.addWidget(server_protocol, 0, 1)
|
||
|
grid.addWidget(server_host, 0, 2)
|
||
|
grid.addWidget(server_port, 0, 3)
|
||
|
|
||
|
def change_protocol(p):
|
||
12 years ago
|
protocol = protocol_letters[p]
|
||
12 years ago
|
host = unicode(server_host.text())
|
||
12 years ago
|
pp = plist.get(host,DEFAULT_PORTS)
|
||
13 years ago
|
if protocol not in pp.keys():
|
||
|
protocol = pp.keys()[0]
|
||
|
port = pp[protocol]
|
||
12 years ago
|
server_host.setText( host )
|
||
|
server_port.setText( port )
|
||
13 years ago
|
|
||
12 years ago
|
server_protocol.connect(server_protocol, SIGNAL('currentIndexChanged(int)'), change_protocol)
|
||
|
|
||
12 years ago
|
label = _('Active Servers') if wallet.interface.servers else _('Default Servers')
|
||
12 years ago
|
servers_list_widget = QTreeWidget(parent)
|
||
12 years ago
|
servers_list_widget.setHeaderLabels( [ label, _('Type') ] )
|
||
12 years ago
|
servers_list_widget.setMaximumHeight(150)
|
||
12 years ago
|
servers_list_widget.setColumnWidth(0, 240)
|
||
12 years ago
|
for _host in servers_list.keys():
|
||
12 years ago
|
_type = 'P' if servers_list[_host].get('pruning') else 'F'
|
||
12 years ago
|
servers_list_widget.addTopLevelItem(QTreeWidgetItem( [ _host, _type ] ))
|
||
13 years ago
|
|
||
12 years ago
|
def change_server(host, protocol=None):
|
||
12 years ago
|
pp = plist.get(host,DEFAULT_PORTS)
|
||
12 years ago
|
if protocol:
|
||
|
port = pp.get(protocol)
|
||
|
if not port: protocol = None
|
||
|
|
||
|
if not protocol:
|
||
12 years ago
|
if 't' in pp.keys():
|
||
12 years ago
|
protocol = 't'
|
||
12 years ago
|
port = pp.get(protocol)
|
||
12 years ago
|
else:
|
||
|
protocol = pp.keys()[0]
|
||
12 years ago
|
port = pp.get(protocol)
|
||
12 years ago
|
|
||
12 years ago
|
server_host.setText( host )
|
||
|
server_port.setText( port )
|
||
12 years ago
|
server_protocol.setCurrentIndex(protocol_letters.index(protocol))
|
||
|
|
||
12 years ago
|
if not plist: return
|
||
12 years ago
|
for p in protocol_letters:
|
||
|
i = protocol_letters.index(p)
|
||
|
j = server_protocol.model().index(i,0)
|
||
|
if p not in pp.keys():
|
||
|
server_protocol.model().setData(j, QtCore.QVariant(0), QtCore.Qt.UserRole-1)
|
||
|
else:
|
||
|
server_protocol.model().setData(j, QtCore.QVariant(0,False), QtCore.Qt.UserRole-1)
|
||
|
|
||
13 years ago
|
|
||
12 years ago
|
if server:
|
||
|
host, port, protocol = server.split(':')
|
||
|
change_server(host,protocol)
|
||
|
|
||
12 years ago
|
servers_list_widget.connect(servers_list_widget, SIGNAL('itemClicked(QTreeWidgetItem*, int)'), lambda x: change_server(unicode(x.text(0))))
|
||
12 years ago
|
grid.addWidget(servers_list_widget, 1, 1, 1, 3)
|
||
|
|
||
12 years ago
|
if not wallet.config.is_modifiable('server'):
|
||
|
for w in [server_host, server_port, server_protocol, servers_list_widget]: w.setEnabled(False)
|
||
|
|
||
12 years ago
|
# auto cycle
|
||
12 years ago
|
autocycle_cb = QCheckBox(_('Try random servers if disconnected'))
|
||
12 years ago
|
autocycle_cb.setChecked(wallet.config.get('auto_cycle', False))
|
||
|
grid.addWidget(autocycle_cb, 3, 1, 3, 2)
|
||
|
if not wallet.config.is_modifiable('auto_cycle'): autocycle_cb.setEnabled(False)
|
||
|
|
||
12 years ago
|
# proxy setting
|
||
13 years ago
|
proxy_mode = QComboBox()
|
||
|
proxy_host = QLineEdit()
|
||
13 years ago
|
proxy_host.setFixedWidth(200)
|
||
13 years ago
|
proxy_port = QLineEdit()
|
||
12 years ago
|
proxy_port.setFixedWidth(60)
|
||
13 years ago
|
proxy_mode.addItems(['NONE', 'SOCKS4', 'SOCKS5', 'HTTP'])
|
||
12 years ago
|
|
||
12 years ago
|
def check_for_disable(index = False):
|
||
|
if proxy_mode.currentText() != 'NONE':
|
||
|
proxy_host.setEnabled(True)
|
||
|
proxy_port.setEnabled(True)
|
||
|
else:
|
||
|
proxy_host.setEnabled(False)
|
||
|
proxy_port.setEnabled(False)
|
||
|
|
||
|
check_for_disable()
|
||
|
proxy_mode.connect(proxy_mode, SIGNAL('currentIndexChanged(int)'), check_for_disable)
|
||
|
|
||
12 years ago
|
if not wallet.config.is_modifiable('proxy'):
|
||
|
for w in [proxy_host, proxy_port, proxy_mode]: w.setEnabled(False)
|
||
|
|
||
12 years ago
|
proxy_config = interface.proxy if interface.proxy else { "mode":"none", "host":"localhost", "port":"8080"}
|
||
|
proxy_mode.setCurrentIndex(proxy_mode.findText(str(proxy_config.get("mode").upper())))
|
||
|
proxy_host.setText(proxy_config.get("host"))
|
||
|
proxy_port.setText(proxy_config.get("port"))
|
||
|
|
||
12 years ago
|
grid.addWidget(QLabel(_('Proxy') + ':'), 2, 0)
|
||
|
grid.addWidget(proxy_mode, 2, 1)
|
||
|
grid.addWidget(proxy_host, 2, 2)
|
||
|
grid.addWidget(proxy_port, 2, 3)
|
||
13 years ago
|
|
||
12 years ago
|
# buttons
|
||
13 years ago
|
vbox.addLayout(ok_cancel_buttons(d))
|
||
13 years ago
|
d.setLayout(vbox)
|
||
13 years ago
|
|
||
|
if not d.exec_(): return
|
||
|
|
||
12 years ago
|
server = unicode( server_host.text() ) + ':' + unicode( server_port.text() ) + ':' + (protocol_letters[server_protocol.currentIndex()])
|
||
12 years ago
|
if proxy_mode.currentText() != 'NONE':
|
||
|
proxy = { u'mode':unicode(proxy_mode.currentText()).lower(), u'host':unicode(proxy_host.text()), u'port':unicode(proxy_port.text()) }
|
||
|
else:
|
||
|
proxy = None
|
||
12 years ago
|
|
||
12 years ago
|
wallet.config.set_key("proxy", proxy, True)
|
||
|
wallet.config.set_key("server", server, True)
|
||
|
interface.set_server(server, proxy)
|
||
12 years ago
|
wallet.config.set_key('auto_cycle', autocycle_cb.isChecked(), True)
|
||
13 years ago
|
return True
|
||
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()
|
||
|
self.config.set_key("column-widths", self.column_widths, True)
|
||
12 years ago
|
self.config.set_key("console-history",self.console.history[-50:])
|
||
13 years ago
|
event.accept()
|
||
13 years ago
|
|
||
|
|
||
13 years ago
|
class ElectrumGui:
|
||
13 years ago
|
|
||
12 years ago
|
def __init__(self, wallet, config, app=None):
|
||
13 years ago
|
self.wallet = wallet
|
||
12 years ago
|
self.config = config
|
||
13 years ago
|
if app is None:
|
||
|
self.app = QApplication(sys.argv)
|
||
13 years ago
|
|
||
13 years ago
|
|
||
13 years ago
|
def restore_or_create(self):
|
||
13 years ago
|
msg = _("Wallet file not found.")+"\n"+_("Do you want to create a new wallet, or to restore an existing one?")
|
||
13 years ago
|
r = QMessageBox.question(None, _('Message'), msg, _('Create'), _('Restore'), _('Cancel'), 0, 2)
|
||
12 years ago
|
if r==2: return None
|
||
|
return 'restore' if r==1 else 'create'
|
||
|
|
||
|
def seed_dialog(self):
|
||
|
return ElectrumWindow.seed_dialog( self.wallet )
|
||
|
|
||
|
def network_dialog(self):
|
||
|
return ElectrumWindow.network_dialog( self.wallet, parent=None )
|
||
13 years ago
|
|
||
12 years ago
|
|
||
|
def show_seed(self):
|
||
|
ElectrumWindow.show_seed_dialog(self.wallet)
|
||
|
|
||
|
|
||
|
def password_dialog(self):
|
||
|
ElectrumWindow.change_password_dialog(self.wallet)
|
||
13 years ago
|
|
||
12 years ago
|
|
||
|
def restore_wallet(self):
|
||
|
wallet = self.wallet
|
||
12 years ago
|
# wait until we are connected, because the user might have selected another server
|
||
|
if not wallet.interface.is_connected:
|
||
12 years ago
|
waiting = lambda: False if wallet.interface.is_connected else "%s \n" % (_("Connecting..."))
|
||
12 years ago
|
waiting_dialog(waiting)
|
||
|
|
||
12 years ago
|
waiting = lambda: False if wallet.is_up_to_date() else "%s\n%s %d\n%s %.1f"\
|
||
12 years ago
|
%(_("Please wait..."),_("Addresses generated:"),len(wallet.addresses(True)),_("Kilobytes received:"), wallet.interface.bytes_received/1024.)
|
||
12 years ago
|
|
||
12 years ago
|
wallet.set_up_to_date(False)
|
||
12 years ago
|
wallet.interface.poke('synchronizer')
|
||
|
waiting_dialog(waiting)
|
||
|
if wallet.is_found():
|
||
12 years ago
|
print_error( "Recovery successful" )
|
||
13 years ago
|
else:
|
||
12 years ago
|
QMessageBox.information(None, _('Error'), _("No transactions found for this seed"), _('OK'))
|
||
13 years ago
|
|
||
|
return True
|
||
|
|
||
13 years ago
|
def main(self,url):
|
||
13 years ago
|
s = Timer()
|
||
13 years ago
|
s.start()
|
||
12 years ago
|
w = ElectrumWindow(self.wallet, self.config)
|
||
13 years ago
|
if url: w.set_url(url)
|
||
13 years ago
|
w.app = self.app
|
||
13 years ago
|
w.connect_slots(s)
|
||
13 years ago
|
w.update_wallet()
|
||
13 years ago
|
w.show()
|
||
13 years ago
|
|
||
13 years ago
|
self.app.exec_()
|
||
12 years ago
|
|
||
|
|