Skip to content

Commit

Permalink
Horizontal layout (#7)
Browse files Browse the repository at this point in the history
* tweaks

* more cleanup

* add horizontal to send tab and also cleanup

* add tooltip

Co-authored-by: Michael Flaxman <[email protected]>
  • Loading branch information
mflaxman and Michael Flaxman authored Nov 18, 2020
1 parent 44f6616 commit 107b922
Show file tree
Hide file tree
Showing 5 changed files with 107 additions and 51 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ GUI version of CLI [multiwallet](https://twitter.com/mflaxman/status/13215030367

## Install

#### Telegram Community Chat Group
Ask Qs here:
<https://t.me/multiwallet>

### Pillow (for QR Codes)

Mac:
Expand Down Expand Up @@ -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:
Expand Down
65 changes: 43 additions & 22 deletions multiwallet_gui/receive.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from PyQt5.QtWidgets import (
QApplication,
QHBoxLayout,
QVBoxLayout,
QWidget,
QLabel,
Expand Down Expand Up @@ -110,7 +111,8 @@ class ReceiveTab(QWidget):

def __init__(self):
super().__init__()
vbox = QVBoxLayout()

vbox = QVBoxLayout(self)

self.descriptorLabel = QLabel("<b>Wallet Descriptor</b>")
self.descriptorLabel.setToolTip(
Expand All @@ -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("<b>Limit of Addresses to Derive</b>")
self.limit_label.setToolTip("Address derivation is slow.")
self.addresses_label = QLabel("<b>Addresses to Derive</b>")
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("<b>Limit</b>")
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("<b>Offset of Addresses to Derive</b>")
self.offset_label = QLabel("<b>Offset</b>")
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."
"<br/><br/>"
"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?

Expand All @@ -187,7 +208,7 @@ def process_submit(self):
results_label += "<br>(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()
Expand All @@ -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)
43 changes: 28 additions & 15 deletions multiwallet_gui/seedpicker.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
)

from PyQt5.QtWidgets import (
QHBoxLayout,
QLabel,
QPlainTextEdit,
QPushButton,
Expand Down Expand Up @@ -50,7 +51,8 @@ class SeedpickerTab(QWidget):

def __init__(self):
super().__init__()
self.layout = QVBoxLayout()

vbox = QVBoxLayout(self)

self.firstWordsLabel = QLabel("<b>First 23 Words of Your Seed Phrase</b>")
self.firstWordsLabel.setToolTip(
Expand All @@ -66,6 +68,8 @@ def __init__(self):
self.button_label = QLabel("<b>Bitcoin Network</b>")
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)
Expand All @@ -74,49 +78,58 @@ 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 <b>offline</b> and store in a <b>secure</b> place. This represents your bitcoin <i>private</i> keys."
"Write the full mnemonic <b>offline</b> and store in a <b>secure</b> place."
"<br/><br/>"
"This represents your bitcoin <i>private</i> keys."
)
self.privResultsEdit = QPlainTextEdit("")
self.privResultsEdit.setReadOnly(True)
self.privResultsEdit.setHidden(True)

self.pubResultsLabel = QLabel("")
self.pubResultsLabel.setToolTip(
"For export to your online computer and eventaully other hardware wallets. This represents your bitcoin <i>public</i> keys, which are neccesary-but-not-sufficient to spend your bitcoin."
"For export to your online computer and eventaully other hardware wallets."
"<br/><br/>"
"This represents your bitcoin <i>public</i> keys, which are neccesary-but-not-sufficient to spend your bitcoin."
)
self.pubResultsROEdit = QPlainTextEdit("")
self.pubResultsROEdit.setReadOnly(True)
self.pubResultsROEdit.setHidden(True)

self.qrButton = QPushButton()
self.qrButton.setText("QR")
self.qrButton.setToolTip(
"For transmitting to your online computer via webcam."
"<br/><br/>"
"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,
self.pubResultsLabel,
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
Expand Down Expand Up @@ -187,12 +200,12 @@ def process_submit(self):
),
]

self.privResultsLabel.setText("<b>SECRET INFO</b> - guard this very carefully")
self.privResultsLabel.setText("<b>SECRET INFO</b>")
self.privResultsEdit.setHidden(False)
self.privResultsEdit.appendPlainText("\n".join(priv_to_display))

pubkey_results_text = (
f"<b>PUBLIC KEY INFO</b> - {'Testnet' if self.IS_TESTNET else 'Mainnet'}"
f"<b>PUBLIC KEY INFO</b> - {'testnet' if self.IS_TESTNET else 'mainnet'}"
)
self.pubResultsLabel.setText(pubkey_results_text)
self.pubResultsROEdit.setHidden(False)
Expand Down
42 changes: 30 additions & 12 deletions multiwallet_gui/send.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@
qr_dialog,
)
from PyQt5.QtWidgets import (
QVBoxLayout,
QWidget,
QHBoxLayout,
QLabel,
QPlainTextEdit,
QPushButton,
QRadioButton,
QVBoxLayout,
QWidget,
)


Expand Down Expand Up @@ -50,7 +51,7 @@ class SendTab(QWidget):
def __init__(self):
super().__init__()

vbox = QVBoxLayout()
vbox = QVBoxLayout(self)

self.psbtLabel = QLabel(
"<b>Partially Signed Bitcoin Transaction</b> (required)"
Expand All @@ -63,10 +64,12 @@ def __init__(self):

# Network toggle
# https://www.tutorialspoint.com/pyqt/pyqt_qradiobutton_widget.htm
self.button_label = QLabel("<b>Bitcoin Network</b>")
self.button_label.setToolTip(BITCOIN_NETWORK_TOOLTIP)
self.network_label = QLabel("<b>Bitcoin Network</b>")
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."
"<br/><br/>"
Expand All @@ -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("<b>Full 24-Word Seed Phrase</b> (optional)")
self.fullSeedLabel = QLabel("<b>Full 24-Word Seed Phrase</b>")
self.fullSeedLabel.setToolTip(
"Needed to sign the PSBT. You can first decode the transaction and inspect it without supplying your seed phrase."
)
Expand All @@ -109,24 +119,32 @@ 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 <i>m-of-n</i> signatures)."
)
self.psbtSignedROEdit = QPlainTextEdit("")
self.psbtSignedROEdit.setReadOnly(True)
self.psbtSignedROEdit.setHidden(True)

self.qrButton = QPushButton()
self.qrButton.setText("QR")
self.qrButton.setToolTip(
"For transmitting to your online computer via webcam."
"<br/><br/>"
"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,
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

setup(
name="multiwallet",
version="0.3.8",
version="0.3.9",
author="Michael Flaxman",
author_email="[email protected]",
description="Stateless multisig bitcoin wallet",
Expand Down

0 comments on commit 107b922

Please sign in to comment.