From 107b92207e8a4c813d0be2e6b1bbc315bcfe47c8 Mon Sep 17 00:00:00 2001 From: Michael Flaxman <448644+mflaxman@users.noreply.github.com> Date: Wed, 18 Nov 2020 15:15:38 -0600 Subject: [PATCH] Horizontal layout (#7) * tweaks * more cleanup * add horizontal to send tab and also cleanup * add tooltip Co-authored-by: Michael Flaxman --- README.md | 6 +++- multiwallet_gui/receive.py | 65 +++++++++++++++++++++++------------ multiwallet_gui/seedpicker.py | 43 +++++++++++++++-------- multiwallet_gui/send.py | 42 +++++++++++++++------- setup.py | 2 +- 5 files changed, 107 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 14c1c4e..d203754 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,10 @@ GUI version of CLI [multiwallet](https://twitter.com/mflaxman/status/13215030367 ## Install +#### Telegram Community Chat Group +Ask Qs here: + + ### Pillow (for QR Codes) Mac: @@ -46,7 +50,7 @@ cd multiwallet python3 -m virtualenv .venv3 source .venv3/bin/activate python3 setup.py develop -python multiwallet_gui/app.py +python3 multiwallet_gui/app.py ``` ## Roadmap: diff --git a/multiwallet_gui/receive.py b/multiwallet_gui/receive.py index 4253c31..8e71034 100644 --- a/multiwallet_gui/receive.py +++ b/multiwallet_gui/receive.py @@ -4,6 +4,7 @@ from PyQt5.QtWidgets import ( QApplication, + QHBoxLayout, QVBoxLayout, QWidget, QLabel, @@ -110,7 +111,8 @@ class ReceiveTab(QWidget): def __init__(self): super().__init__() - vbox = QVBoxLayout() + + vbox = QVBoxLayout(self) self.descriptorLabel = QLabel("Wallet Descriptor") self.descriptorLabel.setToolTip( @@ -122,45 +124,64 @@ def __init__(self): "Something like this:\n\nwsh(sortedmulti(2,[deadbeef/48h/1h/0h/2h]xpub.../0/*," ) - self.limit_label = QLabel("Limit of Addresses to Derive") - self.limit_label.setToolTip("Address derivation is slow.") + self.addresses_label = QLabel("Addresses to Derive") + self.addresses_label.setToolTip( + "Address derivation without libsecp256k1 installed is slow, you may want to be targetted about which addresses to derive." + ) + + hbox = QHBoxLayout(self) + + self.limit_label = QLabel("Limit") + self.limit_label.setToolTip("The number of addresses to dervive.") self.limit_box = QSpinBox() self.limit_box.setValue(5) self.limit_box.setRange(1, 10000) - self.offset_label = QLabel("Offset of Addresses to Derive") + self.offset_label = QLabel("Offset") self.offset_label.setToolTip("Advanced users only.") self.offset_box = QSpinBox() self.offset_box.setValue(0) self.offset_box.setMinimum(0) + for widget in ( + self.limit_label, + self.limit_box, + self.offset_label, + self.offset_box, + ): + hbox.addWidget(widget) + self.descriptorSubmitButton = QPushButton("Derive Addresses") self.descriptorSubmitButton.clicked.connect(self.process_submit) self.addrResultsLabel = QLabel("") self.addrResultsLabel.setToolTip( - "These bitcoin addresses belong to the quorum of extended public keys above. You may want to print this out for future reference." + "These bitcoin addresses belong to the quorum of extended public keys above." + "

" + "You may want to print this out for future reference." ) - self.addrResultsEdit = QPlainTextEdit("") - self.addrResultsEdit.setReadOnly(True) - self.addrResultsEdit.setHidden(True) - - vbox.addWidget(self.descriptorLabel) - vbox.addWidget(self.descriptorEdit) - vbox.addWidget(self.limit_label) - vbox.addWidget(self.limit_box) - vbox.addWidget(self.offset_label) - vbox.addWidget(self.offset_box) - vbox.addWidget(self.descriptorSubmitButton) - vbox.addWidget(self.addrResultsLabel) - vbox.addWidget(self.addrResultsEdit) + self.addrResultsROEdit = QPlainTextEdit("") + self.addrResultsROEdit.setReadOnly(True) + self.addrResultsROEdit.setHidden(True) + + for widget in self.descriptorLabel, self.descriptorEdit, self.addresses_label: + vbox.addWidget(widget) + + vbox.addLayout(hbox) + + for widget in ( + self.descriptorSubmitButton, + self.addrResultsLabel, + self.addrResultsROEdit, + ): + vbox.addWidget(widget) self.setLayout(vbox) def process_submit(self): # Clear any previous submission in case of errors - self.addrResultsEdit.clear() - self.addrResultsEdit.setHidden(True) + self.addrResultsROEdit.clear() + self.addrResultsROEdit.setHidden(True) self.addrResultsLabel.setText("") # TODO: why setText and not hide? @@ -187,7 +208,7 @@ def process_submit(self): results_label += "
(this is ~100x faster with libsec installed)" self.addrResultsLabel.setText(results_label) - self.addrResultsEdit.setHidden(False) + self.addrResultsROEdit.setHidden(False) limit = self.limit_box.value() offset = self.offset_box.value() @@ -202,5 +223,5 @@ def process_submit(self): is_testnet=pubkeys_info["is_testnet"], ): result = f"#{index}: {address}" - self.addrResultsEdit.appendPlainText(result) + self.addrResultsROEdit.appendPlainText(result) QApplication.processEvents() # needed to stream output (otherwise terrible UX) diff --git a/multiwallet_gui/seedpicker.py b/multiwallet_gui/seedpicker.py index 0a80830..55cc1fd 100644 --- a/multiwallet_gui/seedpicker.py +++ b/multiwallet_gui/seedpicker.py @@ -11,6 +11,7 @@ ) from PyQt5.QtWidgets import ( + QHBoxLayout, QLabel, QPlainTextEdit, QPushButton, @@ -50,7 +51,8 @@ class SeedpickerTab(QWidget): def __init__(self): super().__init__() - self.layout = QVBoxLayout() + + vbox = QVBoxLayout(self) self.firstWordsLabel = QLabel("First 23 Words of Your Seed Phrase") self.firstWordsLabel.setToolTip( @@ -66,6 +68,8 @@ def __init__(self): self.button_label = QLabel("Bitcoin Network") self.button_label.setToolTip(BITCOIN_NETWORK_TOOLTIP) + hbox = QHBoxLayout(self) + self.mainnet_button = QRadioButton("Mainnet") self.mainnet_button.setToolTip(BITCOIN_MAINNET_TOOLTIP) self.mainnet_button.setChecked(False) @@ -74,12 +78,17 @@ def __init__(self): self.testnet_button.setToolTip(BITCOIN_TESTNET_TOOLTIP) self.testnet_button.setChecked(True) + for widget in self.mainnet_button, self.testnet_button: + hbox.addWidget(widget) + self.firstWordsSubmitButton = QPushButton("Calculate Full Seed") self.firstWordsSubmitButton.clicked.connect(self.process_submit) self.privResultsLabel = QLabel("") self.privResultsLabel.setToolTip( - "Write the full mnemonic offline and store in a secure place. This represents your bitcoin private keys." + "Write the full mnemonic offline and store in a secure place." + "

" + "This represents your bitcoin private keys." ) self.privResultsEdit = QPlainTextEdit("") self.privResultsEdit.setReadOnly(True) @@ -87,7 +96,9 @@ def __init__(self): self.pubResultsLabel = QLabel("") self.pubResultsLabel.setToolTip( - "For export to your online computer and eventaully other hardware wallets. This represents your bitcoin public keys, which are neccesary-but-not-sufficient to spend your bitcoin." + "For export to your online computer and eventaully other hardware wallets." + "

" + "This represents your bitcoin public keys, which are neccesary-but-not-sufficient to spend your bitcoin." ) self.pubResultsROEdit = QPlainTextEdit("") self.pubResultsROEdit.setReadOnly(True) @@ -95,15 +106,20 @@ def __init__(self): self.qrButton = QPushButton() self.qrButton.setText("QR") + self.qrButton.setToolTip( + "For transmitting to your online computer via webcam." + "

" + "This is a great way to preserve your airgap." + ) self.qrButton.setHidden(True) self.qrButton.clicked.connect(self.make_qr_popup) + for widget in (self.firstWordsLabel, self.firstWordsEdit, self.button_label): + vbox.addWidget(widget) + + vbox.addLayout(hbox) + for widget in ( - self.firstWordsLabel, - self.firstWordsEdit, - self.button_label, - self.mainnet_button, - self.testnet_button, self.firstWordsSubmitButton, self.privResultsLabel, self.privResultsEdit, @@ -111,12 +127,9 @@ def __init__(self): self.pubResultsROEdit, self.qrButton, ): - self.layout.addWidget(widget) - - self.setLayout(self.layout) + vbox.addWidget(widget) - # show all the widgets # TODO: needed? - self.show() + self.setLayout(vbox) def process_submit(self): # Clear any previous submission in case of errors @@ -187,12 +200,12 @@ def process_submit(self): ), ] - self.privResultsLabel.setText("SECRET INFO - guard this very carefully") + self.privResultsLabel.setText("SECRET INFO") self.privResultsEdit.setHidden(False) self.privResultsEdit.appendPlainText("\n".join(priv_to_display)) pubkey_results_text = ( - f"PUBLIC KEY INFO - {'Testnet' if self.IS_TESTNET else 'Mainnet'}" + f"PUBLIC KEY INFO - {'testnet' if self.IS_TESTNET else 'mainnet'}" ) self.pubResultsLabel.setText(pubkey_results_text) self.pubResultsROEdit.setHidden(False) diff --git a/multiwallet_gui/send.py b/multiwallet_gui/send.py index 96c3f8d..d39e162 100644 --- a/multiwallet_gui/send.py +++ b/multiwallet_gui/send.py @@ -10,12 +10,13 @@ qr_dialog, ) from PyQt5.QtWidgets import ( - QVBoxLayout, - QWidget, + QHBoxLayout, QLabel, QPlainTextEdit, QPushButton, QRadioButton, + QVBoxLayout, + QWidget, ) @@ -50,7 +51,7 @@ class SendTab(QWidget): def __init__(self): super().__init__() - vbox = QVBoxLayout() + vbox = QVBoxLayout(self) self.psbtLabel = QLabel( "Partially Signed Bitcoin Transaction (required)" @@ -63,10 +64,12 @@ def __init__(self): # Network toggle # https://www.tutorialspoint.com/pyqt/pyqt_qradiobutton_widget.htm - self.button_label = QLabel("Bitcoin Network") - self.button_label.setToolTip(BITCOIN_NETWORK_TOOLTIP) + self.network_label = QLabel("Bitcoin Network") + self.network_label.setToolTip(BITCOIN_NETWORK_TOOLTIP) + + hbox = QHBoxLayout(self) - self.infernetwork_button = QRadioButton("Smart Guess (default)") + self.infernetwork_button = QRadioButton("Automatic") self.infernetwork_button.setToolTip( "Non-experts should choose this option." "

" @@ -83,10 +86,17 @@ def __init__(self): self.testnet_button.setToolTip(BITCOIN_TESTNET_TOOLTIP) self.testnet_button.setChecked(False) + for widget in ( + self.infernetwork_button, + self.mainnet_button, + self.testnet_button, + ): + hbox.addWidget(widget) + self.psbtSubmitButton = QPushButton("Decode Transaction") self.psbtSubmitButton.clicked.connect(self.decode_psbt) - self.fullSeedLabel = QLabel("Full 24-Word Seed Phrase (optional)") + self.fullSeedLabel = QLabel("Full 24-Word Seed Phrase") self.fullSeedLabel.setToolTip( "Needed to sign the PSBT. You can first decode the transaction and inspect it without supplying your seed phrase." ) @@ -109,7 +119,7 @@ def __init__(self): self.psbtSignedLabel = QLabel("") self.psbtSignedLabel.setToolTip( - "Signed version for your online computer to broadcast to the bitcoin network (once you have collected enough signatures)." + "Signed version for your online computer, which will aggregate signatures and then broadcast to the bitcoin network (once it has the required m-of-n signatures)." ) self.psbtSignedROEdit = QPlainTextEdit("") self.psbtSignedROEdit.setReadOnly(True) @@ -117,16 +127,24 @@ def __init__(self): self.qrButton = QPushButton() self.qrButton.setText("QR") + self.qrButton.setToolTip( + "For transmitting to your online computer via webcam." + "

" + "This is a great way to preserve your airgap." + ) self.qrButton.setHidden(True) self.qrButton.clicked.connect(self.make_qr_popup) for widget in ( self.psbtLabel, self.psbtEdit, - self.button_label, - self.infernetwork_button, - self.mainnet_button, - self.testnet_button, + self.network_label, + ): + vbox.addWidget(widget) + + vbox.addLayout(hbox) + + for widget in ( self.psbtSubmitButton, self.fullSeedLabel, self.fullSeedEdit, diff --git a/setup.py b/setup.py index 156d09d..0e81230 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ setup( name="multiwallet", - version="0.3.8", + version="0.3.9", author="Michael Flaxman", author_email="multiwallet@michaelflaxman.com", description="Stateless multisig bitcoin wallet",