From 16576ca0b218988a14385cc6500319891a0535aa Mon Sep 17 00:00:00 2001 From: Michael Flaxman <448644+mflaxman@users.noreply.github.com> Date: Wed, 18 Nov 2020 08:22:46 -0600 Subject: [PATCH] QR Encode on Seedpicker (#5) * stashing changes * stashing funky changes that sort of resize. needs cleanup * stashing working changes * stashing changes on working version * stashing more changes. Needs testing. * bump version * bump version * add qr image * get rid of signed psbt qr for now (need to add bech32 encoding) Co-authored-by: Michael Flaxman --- README.md | 16 ++++++++ multiwallet_gui/helper.py | 72 +++++++++++++++++++++++++++++++++- multiwallet_gui/images/qr.png | Bin 0 -> 289 bytes multiwallet_gui/seedpicker.py | 40 ++++++++++++++----- requirements.txt | 4 +- setup.py | 2 +- 6 files changed, 121 insertions(+), 13 deletions(-) create mode 100644 multiwallet_gui/images/qr.png diff --git a/README.md b/README.md index 4cd5dae..3fcbe37 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,22 @@ GUI version of CLI [multiwallet](https://twitter.com/mflaxman/status/13215030367 ## Install +### Pillow (for QR Codes) + +Mac: +``` +$ brew install libtiff libjpeg webp little-cms2 +``` + +Ubuntu: +``` +$ sudo apt-get install python3-dev python3-setuptools apt-get install libtiff5-dev libjpeg8-dev libopenjp2-7-dev zlib1g-dev libfreetype6-dev liblcms2-dev libwebp-dev tcl8.6-dev tk8.6-dev python3-tk libharfbuzz-dev libfribidi-dev libxcb1-dev +``` + +For more see instructions [here](https://pillow.readthedocs.io/en/latest/installation.html) + +### Multiwallet + #### Easy Downloadable binary link here (MacOS only): diff --git a/multiwallet_gui/helper.py b/multiwallet_gui/helper.py index 4c2784e..4caeef5 100644 --- a/multiwallet_gui/helper.py +++ b/multiwallet_gui/helper.py @@ -1,4 +1,35 @@ -from PyQt5.QtWidgets import QMessageBox +import qrcode +import re + +from io import BytesIO +from PyQt5.QtWidgets import QMessageBox, QDialog, QVBoxLayout, QLabel +from PyQt5.QtGui import QPixmap, QIcon + + +def strip_html(data): + # https://stackoverflow.com/questions/3398852/using-python-remove-html-tags-formatting-from-a-string + p = re.compile(r"<.*?>") + return p.sub("", data) + + +def create_qr_icon(): + return QIcon("multiwallet_gui/images/qr.png") + + +def create_qt_pixmap_qr(text): + """ + How to use this: + label.setText("") + label.setPixmap(create_qt_pixmap_qr(text="foo")) + + https://stackoverflow.com/a/58251630/1754586 + """ + buf = BytesIO() + img = qrcode.make(text) + img.save(buf, "PNG") + qt_pixmap = QPixmap() + qt_pixmap.loadFromData(buf.getvalue(), "PNG") + return qt_pixmap def _clean_submisission(string): @@ -20,6 +51,45 @@ def _msgbox_err(main_text=None, informative_text=None, detailed_text=None): msg.exec_() +class QRPopup(QDialog): + def __init__(self, window_title, qr_text): + super().__init__() + + self.setWindowTitle(window_title) + + self.setMaximumHeight(16777215) + self.setMaximumWidth(16777215) + + self.qr_text = qr_text + + self.vbox = QVBoxLayout() + + self.labelImage = QLabel() + self.labelImage.setMaximumHeight(16777215) + self.labelImage.setMaximumWidth(16777215) + + self._set_pixmap() + + self.vbox.addWidget(self.labelImage) + self.setLayout(self.vbox) + + self.show() + + def _set_pixmap(self): + self.pixmap = create_qt_pixmap_qr(text=self.qr_text).scaledToHeight( + self.height() * 0.9 + ) + self.labelImage.setPixmap(self.pixmap) + + def resizeEvent(self, event): + self.pixmap = self._set_pixmap() + + +def qr_dialog(qr_text, window_title): + dialog = QRPopup(qr_text=qr_text, window_title=strip_html(window_title)) + return dialog.exec_() + + def _is_libsec_enabled(): # TODO: move to buidl try: diff --git a/multiwallet_gui/images/qr.png b/multiwallet_gui/images/qr.png new file mode 100644 index 0000000000000000000000000000000000000000..6cd9cc0c3c5e33f2dba6d68e16a3250623fae2a2 GIT binary patch literal 289 zcmeAS@N?(olHy`uVBq!ia0vp^;ULV&3?#kTy!C+;TYyi9E0F&G|NqjP^X~##OeH~n z!TtbcO$ddCsggwqKpO{Zo^Dy8Sx?$!MDbDHBm=K6=C3KN+>w3$Ej(Cf@B z5v+VA?e}QbyalNj*?&%6m~~d(zkX#@x^-#LSF2AA>zpq?{B$6d?R50Rh#lJZK1fZg zZt&CLe%ey0aCz4JBA3(E{85<#hi|1T`MuD6zq@&v^3#PTHO)0|eP7qP+sofvRrkKU ha?#WHyHR)be^1public keys, which are neccesary-but-not-sufficient to spend your bitcoin." ) - self.pubResultsEdit = QPlainTextEdit("") - self.pubResultsEdit.setReadOnly(True) - self.pubResultsEdit.setHidden(True) + self.pubResultsROEdit = QPlainTextEdit("") + self.pubResultsROEdit.setReadOnly(True) + self.pubResultsROEdit.setHidden(True) + + self.qrButton = QPushButton() + self.qrButton.setText("QR") + self.qrButton.setHidden(True) + self.qrButton.clicked.connect(self.make_qr_popup) for widget in ( self.firstWordsLabel, @@ -102,7 +108,8 @@ def __init__(self): self.privResultsLabel, self.privResultsEdit, self.pubResultsLabel, - self.pubResultsEdit, + self.pubResultsROEdit, + self.qrButton, ): self.layout.addWidget(widget) @@ -116,9 +123,11 @@ def process_submit(self): self.privResultsEdit.clear() self.privResultsEdit.setHidden(True) self.privResultsLabel.setText("") - self.pubResultsEdit.clear() - self.pubResultsEdit.setHidden(True) + self.pubResultsROEdit.clear() + self.pubResultsROEdit.setHidden(True) self.pubResultsLabel.setText("") + self.qrButton.setHidden(True) + self.qrButton.setText("") # TODO: why setText and not hide? first_words = _clean_submisission(self.firstWordsEdit.toPlainText()) @@ -182,8 +191,19 @@ def process_submit(self): self.privResultsEdit.setHidden(False) self.privResultsEdit.appendPlainText("\n".join(priv_to_display)) - self.pubResultsLabel.setText( + pubkey_results_text = ( f"PUBLIC KEY INFO - {'Testnet' if self.IS_TESTNET else 'Mainnet'}" ) - self.pubResultsEdit.setHidden(False) - self.pubResultsEdit.appendPlainText("\n".join(pub_to_display)) + self.pubResultsLabel.setText(pubkey_results_text) + self.pubResultsROEdit.setHidden(False) + self.pubResultsROEdit.appendPlainText("\n".join(pub_to_display)) + + self.qrButton.setHidden(False) + self.qrButton.setText("QR") + self.qrButton.setIcon(create_qr_icon()) + + def make_qr_popup(self): + qr_dialog( + qr_text=self.pubResultsROEdit.toPlainText(), + window_title=self.pubResultsLabel.text(), + ) diff --git a/requirements.txt b/requirements.txt index 8341c87..1724d12 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,4 @@ -buidl==0.2.1 +buidl==0.2.2 PyQt5==5.15.1 +qrcode==6.1 +Pillow==8.0.1 diff --git a/setup.py b/setup.py index afb62ac..a842264 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name="multiwallet", - version="0.3.6", + version="0.3.7", author="Michael Flaxman", author_email="multiwallet@michaelflaxman.com", description="Stateless multisig bitcoin wallet",