Window modality fixes + improved password dialog

This commit is contained in:
Neil Booth 2015-12-23 17:32:08 +09:00
parent 62201b37f5
commit 37b474716b
5 changed files with 131 additions and 164 deletions

View file

@ -407,11 +407,11 @@ class InstallWizard(WindowModalDialog, MessageBoxMixin):
return self.exec_() return self.exec_()
def password_dialog(self): def password_dialog(self):
msg = _("Please choose a password to encrypt your wallet keys.")+'\n'\ from password_dialog import PasswordDialog
+_("Leave these fields empty if you want to disable encryption.") msg = _("Please choose a password to encrypt your wallet keys.\n"
from password_dialog import make_password_dialog, run_password_dialog "Leave these fields empty if you want to disable encryption.")
self.set_layout( make_password_dialog(self, None, msg) ) dialog = PasswordDialog(self, None, _("Choose a password"), msg, True)
return run_password_dialog(self, None, self)[2] return dialog.run()[2]
def run(self, action): def run(self, action):
if self.storage.file_exists and action != 'new': if self.storage.file_exists and action != 'new':

View file

@ -1852,8 +1852,39 @@ class ElectrumWindow(QMainWindow, MessageBoxMixin, PrintError):
def change_password_dialog(self): def change_password_dialog(self):
from password_dialog import PasswordDialog from password_dialog import PasswordDialog
d = PasswordDialog(self.wallet, self)
d.run() if self.wallet and self.wallet.is_watching_only():
self.show_error(_('This is a watching-only wallet'))
return
msg = (_('Your wallet is encrypted. Use this dialog to change your '
'password. To disable wallet encryption, enter an empty new '
'password.') if self.wallet.use_encryption
else _('Your wallet keys are not encrypted'))
d = PasswordDialog(self, self.wallet, _("Set Password"), msg, True)
ok, password, new_password = d.run()
if not ok:
return
try:
self.wallet.check_password(password)
except BaseException as e:
self.show_error(str(e))
return
try:
self.wallet.update_password(password, new_password)
except:
traceback.print_exc(file=sys.stdout)
self.show_error(_('Failed to update password'))
return
if new_password:
msg = _('Password was updated successfully')
else:
msg = _('This wallet is not encrypted')
self.show_message(msg, title=_("Success"))
self.update_lock_icon() self.update_lock_icon()
def toggle_search(self): def toggle_search(self):

View file

@ -23,9 +23,27 @@ from util import *
import re import re
import math import math
def check_password_strength(password):
'''
Check the strength of the password entered by the user and return back the same
:param password: password entered by user in New Password
:return: password strength Weak or Medium or Strong
'''
password = unicode(password)
n = math.log(len(set(password)))
num = re.search("[0-9]", password) is not None and re.match("^[0-9]*$", password) is None
caps = password != password.upper() and password != password.lower()
extra = re.match("^[a-zA-Z0-9]*$", password) is None
score = len(password)*( n + caps + num + extra)/20
password_strength = {0:"Weak",1:"Medium",2:"Strong",3:"Very Strong"}
return password_strength[min(3, int(score))]
def make_password_dialog(self, wallet, msg, new_pass=True): class PasswordDialog(WindowModalDialog):
def __init__(self, parent, wallet, title, msg, new_pass):
WindowModalDialog.__init__(self, parent, title)
self.wallet = wallet
self.pw = QLineEdit() self.pw = QLineEdit()
self.pw.setEchoMode(2) self.pw.setEchoMode(2)
@ -44,8 +62,6 @@ def make_password_dialog(self, wallet, msg, new_pass=True):
grid.setColumnStretch(1,1) grid.setColumnStretch(1,1)
logo = QLabel() logo = QLabel()
lockfile = ":icons/lock.png" if wallet and wallet.use_encryption else ":icons/unlock.png"
logo.setPixmap(QPixmap(lockfile).scaledToWidth(36))
logo.setAlignment(Qt.AlignCenter) logo.setAlignment(Qt.AlignCenter)
grid.addWidget(logo, 0, 0) grid.addWidget(logo, 0, 0)
@ -60,6 +76,11 @@ def make_password_dialog(self, wallet, msg, new_pass=True):
if wallet and wallet.use_encryption: if wallet and wallet.use_encryption:
grid.addWidget(QLabel(_('Password')), 0, 0) grid.addWidget(QLabel(_('Password')), 0, 0)
grid.addWidget(self.pw, 0, 1) grid.addWidget(self.pw, 0, 1)
lockfile = ":icons/lock.png"
else:
self.pw = None
lockfile = ":icons/unlock.png"
logo.setPixmap(QPixmap(lockfile).scaledToWidth(36))
grid.addWidget(QLabel(_('New Password') if new_pass else _('Password')), 1, 0) grid.addWidget(QLabel(_('New Password') if new_pass else _('Password')), 1, 0)
grid.addWidget(self.new_pw, 1, 1) grid.addWidget(self.new_pw, 1, 1)
@ -71,103 +92,36 @@ def make_password_dialog(self, wallet, msg, new_pass=True):
# Password Strength Label # Password Strength Label
self.pw_strength = QLabel() self.pw_strength = QLabel()
grid.addWidget(self.pw_strength, 3, 0, 1, 2) grid.addWidget(self.pw_strength, 3, 0, 1, 2)
self.new_pw.textChanged.connect(lambda: update_password_strength(self.pw_strength, self.new_pw.text())) self.new_pw.textChanged.connect(self.pw_changed)
self.conf_pw.textChanged.connect(self.check_OKButton)
self.OKButton = OkButton(self)
vbox.addStretch(1) vbox.addStretch(1)
vbox.addLayout(Buttons(CancelButton(self), OkButton(self))) vbox.addLayout(Buttons(CancelButton(self), self.OKButton))
return vbox self.setLayout(vbox)
def pw_changed(self):
password = self.new_pw.text()
if password:
colors = {"Weak":"Red", "Medium":"Blue", "Strong":"Green",
"Very Strong":"Green"}
strength = check_password_strength(password)
label = (_("Password Strength") + ": " + "<font color="
+ colors[strength] + ">" + strength + "</font>")
else:
label = ""
self.pw_strength.setText(label)
self.check_OKButton()
def run_password_dialog(self, wallet, parent): def check_OKButton(self):
self.OKButton.setEnabled(self.new_pw.text() == self.conf_pw.text())
if wallet and wallet.is_watching_only():
QMessageBox.information(parent, _('Error'), _('This is a watching-only wallet'), _('OK'))
return False, None, None
def run(self):
if not self.exec_(): if not self.exec_():
return False, None, None return False, None, None
password = unicode(self.pw.text()) if wallet and wallet.use_encryption else None password = unicode(self.pw.text()) if self.pw else None
new_password = unicode(self.new_pw.text()) new_password = unicode(self.new_pw.text())
new_password2 = unicode(self.conf_pw.text()) new_password2 = unicode(self.conf_pw.text())
if new_password != new_password2: return True, password or None, new_password or None
QMessageBox.warning(parent, _('Error'), _('Passwords do not match'), _('OK'))
# Retry
return run_password_dialog(self, wallet, parent)
if not new_password:
new_password = None
return True, password, new_password
def check_password_strength(password):
'''
Check the strength of the password entered by the user and return back the same
:param password: password entered by user in New Password
:return: password strength Weak or Medium or Strong
'''
password = unicode(password)
n = math.log(len(set(password)))
num = re.search("[0-9]", password) is not None and re.match("^[0-9]*$", password) is None
caps = password != password.upper() and password != password.lower()
extra = re.match("^[a-zA-Z0-9]*$", password) is None
score = len(password)*( n + caps + num + extra)/20
password_strength = {0:"Weak",1:"Medium",2:"Strong",3:"Very Strong"}
return password_strength[min(3, int(score))]
def update_password_strength(pw_strength_label,password):
'''
call the function check_password_strength and update the label pw_strength interactively as the user is typing the password
:param pw_strength_label: the label pw_strength
:param password: password entered in New Password text box
:return: None
'''
if password:
colors = {"Weak":"Red","Medium":"Blue","Strong":"Green", "Very Strong":"Green"}
strength = check_password_strength(password)
label = _("Password Strength")+ ": "+"<font color=" + colors[strength] + ">" + strength + "</font>"
else:
label = ""
pw_strength_label.setText(label)
class PasswordDialog(WindowModalDialog):
def __init__(self, wallet, parent):
WindowModalDialog.__init__(self, parent,_("Set Password"))
self.wallet = wallet
self.parent = parent
msg = (_('Your wallet is encrypted. Use this dialog to change your password.') + ' '\
+_('To disable wallet encryption, enter an empty new password.')) \
if wallet.use_encryption else _('Your wallet keys are not encrypted')
self.setLayout(make_password_dialog(self, wallet, msg))
def run(self):
ok, password, new_password = run_password_dialog(self, self.wallet, self.parent)
if not ok:
return
try:
self.wallet.check_password(password)
except BaseException as e:
QMessageBox.warning(self.parent, _('Error'), str(e), _('OK'))
return False, None, None
try:
self.wallet.update_password(password, new_password)
except:
import traceback, sys
traceback.print_exc(file=sys.stdout)
QMessageBox.warning(self.parent, _('Error'), _('Failed to update password'), _('OK'))
return
if new_password:
QMessageBox.information(self.parent, _('Success'), _('Password was updated successfully'), _('OK'))
else:
QMessageBox.information(self.parent, _('Success'), _('This wallet is not encrypted'), _('OK'))

View file

@ -1,4 +1,4 @@
from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton from PyQt4.Qt import QMessageBox, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton
import PyQt4.QtCore as QtCore import PyQt4.QtCore as QtCore
from electrum_gui.qt.util import * from electrum_gui.qt.util import *
from electrum_gui.qt.main_window import StatusBarButton, ElectrumWindow from electrum_gui.qt.main_window import StatusBarButton, ElectrumWindow
@ -73,7 +73,7 @@ class Plugin(KeepKeyPlugin):
return return
get_label = lambda: self.get_client().features.label get_label = lambda: self.get_client().features.label
update_label = lambda: current_label_label.setText("Label: %s" % get_label()) update_label = lambda: current_label_label.setText("Label: %s" % get_label())
d = QDialog() d = WindowModalDialog(window, _("KeepKey Settings"))
layout = QGridLayout(d) layout = QGridLayout(d)
layout.addWidget(QLabel("KeepKey Options"),0,0) layout.addWidget(QLabel("KeepKey Options"),0,0)
layout.addWidget(QLabel("ID:"),1,0) layout.addWidget(QLabel("ID:"),1,0)
@ -132,10 +132,7 @@ class KeepKeyQtHandler:
return self.passphrase return self.passphrase
def pin_dialog(self): def pin_dialog(self):
d = QDialog(None) d = WindowModalDialog(self.win, _("Enter PIN"))
d.setModal(1)
d.setWindowTitle(_("Enter PIN"))
d.setWindowFlags(d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
matrix = PinMatrixWidget() matrix = PinMatrixWidget()
vbox = QVBoxLayout() vbox = QVBoxLayout()
vbox.addWidget(QLabel(self.message)) vbox.addWidget(QLabel(self.message))
@ -153,23 +150,18 @@ class KeepKeyQtHandler:
self.passphrase = unicodedata.normalize('NFKD', unicode(passphrase)) if passphrase else '' self.passphrase = unicodedata.normalize('NFKD', unicode(passphrase)) if passphrase else ''
else: else:
assert type(self.win) is InstallWizard assert type(self.win) is InstallWizard
from electrum_gui.qt.password_dialog import make_password_dialog, run_password_dialog from electrum_gui.qt.password_dialog import PasswordDialog
d = QDialog() d = PasswordDialog(self.win, None, None, self.message, False)
d.setModal(1) confirmed, p, passphrase = d.run()
d.setLayout(make_password_dialog(d, None, self.message, False))
confirmed, p, passphrase = run_password_dialog(d, None, None)
if not confirmed: if not confirmed:
QMessageBox.critical(None, _('Error'), _("Password request canceled"), _('OK')) self.win.show_critical(_("Password request canceled"))
self.passphrase = None self.passphrase = None
else: else:
self.passphrase = unicodedata.normalize('NFKD', unicode(passphrase)) if passphrase else '' self.passphrase = unicodedata.normalize('NFKD', unicode(passphrase)) if passphrase else ''
self.done.set() self.done.set()
def message_dialog(self): def message_dialog(self):
self.d = QDialog() self.d = WindowModalDialog(self.win, _('Please Check KeepKey Device'))
self.d.setModal(1)
self.d.setWindowTitle('Please Check KeepKey Device')
self.d.setWindowFlags(self.d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
l = QLabel(self.message) l = QLabel(self.message)
vbox = QVBoxLayout(self.d) vbox = QVBoxLayout(self.d)
vbox.addWidget(l) vbox.addWidget(l)
@ -182,5 +174,3 @@ class KeepKeyQtHandler:
def dialog_stop(self): def dialog_stop(self):
self.d.hide() self.d.hide()

View file

@ -1,4 +1,4 @@
from PyQt4.Qt import QMessageBox, QDialog, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton from PyQt4.Qt import QMessageBox, QVBoxLayout, QLabel, QThread, SIGNAL, QGridLayout, QInputDialog, QPushButton
import PyQt4.QtCore as QtCore import PyQt4.QtCore as QtCore
from electrum_gui.qt.util import * from electrum_gui.qt.util import *
from electrum_gui.qt.main_window import StatusBarButton, ElectrumWindow from electrum_gui.qt.main_window import StatusBarButton, ElectrumWindow
@ -46,10 +46,7 @@ class TrezorQtHandler:
return self.passphrase return self.passphrase
def pin_dialog(self): def pin_dialog(self):
d = QDialog(None) d = WindowModalDialog(self.win, _("Enter PIN"))
d.setModal(1)
d.setWindowTitle(_("Enter PIN"))
d.setWindowFlags(d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
matrix = PinMatrixWidget() matrix = PinMatrixWidget()
vbox = QVBoxLayout() vbox = QVBoxLayout()
vbox.addWidget(QLabel(self.message)) vbox.addWidget(QLabel(self.message))
@ -67,23 +64,18 @@ class TrezorQtHandler:
self.passphrase = unicodedata.normalize('NFKD', unicode(passphrase)) if passphrase else '' self.passphrase = unicodedata.normalize('NFKD', unicode(passphrase)) if passphrase else ''
else: else:
assert type(self.win) is InstallWizard assert type(self.win) is InstallWizard
from electrum_gui.qt.password_dialog import make_password_dialog, run_password_dialog from electrum_gui.qt.password_dialog import PasswordDialog
d = QDialog() d = PasswordDialog(self.win, None, None, self.message, False)
d.setModal(1) confirmed, p, passphrase = d.run()
d.setLayout(make_password_dialog(d, None, self.message, False))
confirmed, p, passphrase = run_password_dialog(d, None, None)
if not confirmed: if not confirmed:
QMessageBox.critical(None, _('Error'), _("Password request canceled"), _('OK')) self.win.show_critical(_("Password request canceled"))
self.passphrase = None self.passphrase = None
else: else:
self.passphrase = unicodedata.normalize('NFKD', unicode(passphrase)) if passphrase else '' self.passphrase = unicodedata.normalize('NFKD', unicode(passphrase)) if passphrase else ''
self.done.set() self.done.set()
def message_dialog(self): def message_dialog(self):
self.d = QDialog() self.d = WindowModalDialog(self.win, _('Please Check Trezor Device'))
self.d.setModal(1)
self.d.setWindowTitle('Please Check Trezor Device')
self.d.setWindowFlags(self.d.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
l = QLabel(self.message) l = QLabel(self.message)
vbox = QVBoxLayout(self.d) vbox = QVBoxLayout(self.d)
vbox.addWidget(l) vbox.addWidget(l)
@ -171,7 +163,7 @@ class Plugin(TrezorPlugin):
return return
get_label = lambda: self.get_client().features.label get_label = lambda: self.get_client().features.label
update_label = lambda: current_label_label.setText("Label: %s" % get_label()) update_label = lambda: current_label_label.setText("Label: %s" % get_label())
d = QDialog() d = WindowModalDialog(window, _("Trezor Settings"))
layout = QGridLayout(d) layout = QGridLayout(d)
layout.addWidget(QLabel("Trezor Options"),0,0) layout.addWidget(QLabel("Trezor Options"),0,0)
layout.addWidget(QLabel("ID:"),1,0) layout.addWidget(QLabel("ID:"),1,0)