mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-29 09:38:08 +00:00
b4c5b996a1
- Try Qt5 before Qt4. - Handle all exceptions (a ValueError is thrown if the wrong Qt was imported earlier)
175 lines
5.2 KiB
Python
175 lines
5.2 KiB
Python
# This file is part of the Trezor project.
|
|
#
|
|
# Copyright (C) 2012-2018 SatoshiLabs and contributors
|
|
#
|
|
# This library is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Lesser General Public License version 3
|
|
# as published by the Free Software Foundation.
|
|
#
|
|
# This library 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 Lesser General Public License for more details.
|
|
#
|
|
# You should have received a copy of the License along with this library.
|
|
# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
|
|
|
|
import math
|
|
import sys
|
|
|
|
try:
|
|
from PyQt5.QtWidgets import (
|
|
QPushButton,
|
|
QLineEdit,
|
|
QSizePolicy,
|
|
QLabel,
|
|
QApplication,
|
|
QWidget,
|
|
QGridLayout,
|
|
QVBoxLayout,
|
|
QHBoxLayout,
|
|
)
|
|
from PyQt5.QtGui import QRegExpValidator
|
|
from PyQt5.QtCore import QRegExp, Qt
|
|
from PyQt5.Qt import QT_VERSION_STR
|
|
except Exception:
|
|
from PyQt4.QtGui import (
|
|
QPushButton,
|
|
QLineEdit,
|
|
QSizePolicy,
|
|
QRegExpValidator,
|
|
QLabel,
|
|
QApplication,
|
|
QWidget,
|
|
QGridLayout,
|
|
QVBoxLayout,
|
|
QHBoxLayout,
|
|
)
|
|
from PyQt4.QtCore import QObject, SIGNAL, QRegExp, Qt, QT_VERSION_STR
|
|
|
|
|
|
class PinButton(QPushButton):
|
|
def __init__(self, password, encoded_value):
|
|
super(PinButton, self).__init__("?")
|
|
self.password = password
|
|
self.encoded_value = encoded_value
|
|
|
|
if QT_VERSION_STR >= "5":
|
|
self.clicked.connect(self._pressed)
|
|
elif QT_VERSION_STR >= "4":
|
|
QObject.connect(self, SIGNAL("clicked()"), self._pressed)
|
|
else:
|
|
raise RuntimeError("Unsupported Qt version")
|
|
|
|
def _pressed(self):
|
|
self.password.setText(self.password.text() + str(self.encoded_value))
|
|
self.password.setFocus()
|
|
|
|
|
|
class PinMatrixWidget(QWidget):
|
|
"""
|
|
Displays widget with nine blank buttons and password box.
|
|
Encodes button clicks into sequence of numbers for passing
|
|
into PinAck messages of TREZOR.
|
|
|
|
show_strength=True may be useful for entering new PIN
|
|
"""
|
|
|
|
def __init__(self, show_strength=True, parent=None):
|
|
super(PinMatrixWidget, self).__init__(parent)
|
|
|
|
self.password = QLineEdit()
|
|
self.password.setValidator(QRegExpValidator(QRegExp("[1-9]+"), None))
|
|
self.password.setEchoMode(QLineEdit.Password)
|
|
|
|
if QT_VERSION_STR >= "5":
|
|
self.password.textChanged.connect(self._password_changed)
|
|
elif QT_VERSION_STR >= "4":
|
|
QObject.connect(
|
|
self.password, SIGNAL("textChanged(QString)"), self._password_changed
|
|
)
|
|
else:
|
|
raise RuntimeError("Unsupported Qt version")
|
|
|
|
self.strength = QLabel()
|
|
self.strength.setMinimumWidth(75)
|
|
self.strength.setAlignment(Qt.AlignCenter)
|
|
self._set_strength(0)
|
|
|
|
grid = QGridLayout()
|
|
grid.setSpacing(0)
|
|
for y in range(3)[::-1]:
|
|
for x in range(3):
|
|
button = PinButton(self.password, x + y * 3 + 1)
|
|
button.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
|
|
button.setFocusPolicy(Qt.NoFocus)
|
|
grid.addWidget(button, 3 - y, x)
|
|
|
|
hbox = QHBoxLayout()
|
|
hbox.addWidget(self.password)
|
|
if show_strength:
|
|
hbox.addWidget(self.strength)
|
|
|
|
vbox = QVBoxLayout()
|
|
vbox.addLayout(grid)
|
|
vbox.addLayout(hbox)
|
|
self.setLayout(vbox)
|
|
|
|
def _set_strength(self, strength):
|
|
if strength < 3000:
|
|
self.strength.setText("weak")
|
|
self.strength.setStyleSheet("QLabel { color : #d00; }")
|
|
elif strength < 60000:
|
|
self.strength.setText("fine")
|
|
self.strength.setStyleSheet("QLabel { color : #db0; }")
|
|
elif strength < 360000:
|
|
self.strength.setText("strong")
|
|
self.strength.setStyleSheet("QLabel { color : #0a0; }")
|
|
else:
|
|
self.strength.setText("ULTIMATE")
|
|
self.strength.setStyleSheet("QLabel { color : #000; font-weight: bold;}")
|
|
|
|
def _password_changed(self, password):
|
|
self._set_strength(self.get_strength())
|
|
|
|
def get_strength(self):
|
|
digits = len(set(str(self.password.text())))
|
|
strength = math.factorial(9) / math.factorial(9 - digits)
|
|
return strength
|
|
|
|
def get_value(self):
|
|
return self.password.text()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
"""
|
|
Demo application showing PinMatrix widget in action
|
|
"""
|
|
app = QApplication(sys.argv)
|
|
|
|
matrix = PinMatrixWidget()
|
|
|
|
def clicked():
|
|
print("PinMatrix value is", matrix.get_value())
|
|
print("Possible button combinations:", matrix.get_strength())
|
|
sys.exit()
|
|
|
|
ok = QPushButton("OK")
|
|
if QT_VERSION_STR >= "5":
|
|
ok.clicked.connect(clicked)
|
|
elif QT_VERSION_STR >= "4":
|
|
QObject.connect(ok, SIGNAL("clicked()"), clicked)
|
|
else:
|
|
raise RuntimeError("Unsupported Qt version")
|
|
|
|
vbox = QVBoxLayout()
|
|
vbox.addWidget(matrix)
|
|
vbox.addWidget(ok)
|
|
|
|
w = QWidget()
|
|
w.setLayout(vbox)
|
|
w.move(100, 100)
|
|
w.show()
|
|
|
|
app.exec_()
|