Commit Graph

3804 Commits

Author SHA1 Message Date
ghost43
c3b9ef0c89 Merge pull request #10034 from f321x/fix_confirm_tx_dialog_timer_exc
fix: qt: handle main_window.gui_object.timer being None
2025-07-16 12:17:24 +00:00
Sander van Grieken
0e055f8127 qt: don't share ElectrumGui.QTimer, use self-contained QTimer so its lifecycle is synced in
`ElectrumWindow`, `TxEditor`, `SwapDialog`

Also use `QTimer` classmethod for `.singleShot()` occurrences.
2025-07-16 13:13:26 +02:00
Sander van Grieken
b9ea17674d qml: fix passphrase/seed extension wordwrap 2025-07-16 13:05:44 +02:00
f321x
121b7b767e fix: qt: handle main_window.gui_object.timer being None
When closing Electrum with open `ConfirmTxDialog` the following
exception is raised:

```
1319.20 | E | gui.qt.exception_window.Exception_Hook | exception caught by crash reporter
Traceback (most recent call last):
  File "/home/user/code/electrum-fork/electrum/gui/qt/send_tab.py", line 575, in do_pay_or_get_invoice
    self.do_pay_invoice(self.pending_invoice)
    ~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/code/electrum-fork/electrum/gui/qt/send_tab.py", line 602, in do_pay_invoice
    self.pay_onchain_dialog(invoice.outputs, invoice=invoice)
    ~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/code/electrum-fork/electrum/gui/qt/send_tab.py", line 328, in pay_onchain_dialog
    tx, is_preview = self.window.confirm_tx_dialog(make_tx, output_value, batching_candidates=candidates)
                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/user/code/electrum-fork/electrum/gui/qt/main_window.py", line 1502, in confirm_tx_dialog
    return d.run(), d.is_preview
           ~~~~~^^
  File "/home/user/code/electrum-fork/electrum/gui/qt/confirm_tx_dialog.py", line 477, in run
    self.stop_editor_updates()
    ~~~~~~~~~~~~~~~~~~~~~~~~^^
  File "/home/user/code/electrum-fork/electrum/gui/qt/confirm_tx_dialog.py", line 133, in stop_editor_updates
    self.main_window.gui_object.timer.timeout.disconnect(self.timer_actions)
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'timeout'
```

This can be prevented by checking if `main_window.gui_object.timer` is
None before trying to disconnect it.
2025-07-16 09:47:32 +02:00
SomberNight
76005372a6 qt ConfirmTxDialog: only show fee_target text if slider is active 2025-07-15 16:52:11 +00:00
SomberNight
02b0073ca5 qt ConfirmTxDialog: also save custom fixed feerate fee_policies
- save custom fixed feerate fee_policies, not just slider-restricted ones
- dynamically update fee_target text, to follow feerate_e
  - otherwise when the slider is set to "feerate", users would be shown two conflicting values
2025-07-15 16:45:29 +00:00
SomberNight
68a203336e qt ConfirmTxDialog: fix feerate_e 2025-07-15 16:43:53 +00:00
ThomasV
6d22c24deb Merge pull request #10028 from SomberNight/202507_issue10021
qt: only allow wallet unlock if wallet has ks-enc
2025-07-15 15:30:33 +02:00
SomberNight
482d573f55 stdio gui: use daemon.load_wallet(), similar to text gui 2025-07-15 13:23:50 +00:00
SomberNight
f2f1dddcc8 swaps: factor out pubkey_to_rgb_color into core lib 2025-07-14 21:09:18 +00:00
SomberNight
9b5b2bad4a qt: only allow wallet unlock if wallet has ks-enc
fixes https://github.com/spesmilo/electrum/issues/10021
2025-07-14 14:26:14 +00:00
Sander van Grieken
78a7c85f49 qml: swap: like on desktop, use pkh of nostr pubkey as color 2025-07-11 16:33:40 +02:00
accumulator
62a30c5688 Merge pull request #10017 from f321x/fix_qml_wizard_exception
fix: qml: wizard: delete seed_type from wizard_data if not set explicitly
2025-07-11 11:52:05 +02:00
accumulator
f87243dd5c Merge pull request #10014 from f321x/fix_qml_label_setting
fix: qml: update tx label on detailsChanged signal
2025-07-11 10:50:27 +02:00
SomberNight
0f4d8d6d57 follow-up prev: fix weird pyqt bug re QPushButton.clicked
When clicking the "close_button" button (labelled "Not Now"), the whole app state got bugged:
none of the existing windows (e.g. ElectrumWindow) could get focus anymore.

The signature of PushButton.clicked is:
`void QAbstractButton::clicked(bool checked = false)`
https://doc.qt.io/qt-6/qabstractbutton.html#clicked

and pyqt is probably doing some polymorphism, dynamically deciding if the qt slot wants the "checked" arg or not.

The extremely weird part is that the bug is triggered on clicking "close_button" (probably pyqt is passing "checked" to self.close)
but instead of the current commit, the following diff, touching a completely different button, would also "fix" the issue:

```
diff --git a/electrum/gui/qt/exception_window.py b/electrum/gui/qt/exception_window.py
index eceab89de6..e0162e5827 100644
--- a/electrum/gui/qt/exception_window.py
+++ b/electrum/gui/qt/exception_window.py
@@ -67,7 +67,7 @@ class Exception_Window(BaseCrashReporter, QWidget, MessageBoxMixin, Logger):

         self._report_contents_dlg = None  # type: Optional[ReportContentsDialog]
         collapse_info = QPushButton(_("Show report contents"))
-        collapse_info.clicked.connect(self.show_report_contents_dlg)
+        collapse_info.clicked.connect(lambda: self.show_report_contents_dlg())

         main_box.addWidget(collapse_info)
 ```

No idea why.
2025-07-09 15:32:07 +00:00
f321x
aefb180007 fix: qml: wizard: delete seed_type if not set
deletes the `seed_type` key from `wizard_data` in `WCWalletType` if it
is not explicitly set to prevent a stale value from a previous wizard
flow if the user goes back in the wizard and selects a different wallet
type instead of completing the wizard with the previously selected
wallet type.
This happens as the `apply()` function gets called with the
previously set radio button (e.g. 2fa) if the user goes back, if he then
selects multisig the `2fa_segwit` `seed_type` won't get cleared and
cause the exception later.
Example exception when first selecting 2fa, then going back and creating
a multisig wallet:
```
32.77 | E | gui.qml.qeapp.Exception_Hook | exception caught by crash reporter
Traceback (most recent call last):
  File "/home/vagrant/electrum/electrum/gui/qml/qewizard.py", line 40, in submit
    view = self.resolve_next(self._current.view, wdata)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/vagrant/electrum/electrum/wizard.py", line 78, in resolve_next
    view_accept(wizard_data)
  File "/home/vagrant/electrum/electrum/wizard.py", line 501, in maybe_master_pubkey
    wizard_data['multisig_master_pubkey'] = self.keystore_from_data(wizard_data['wallet_type'], wizard_data).get_master_public_key()
                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/vagrant/electrum/electrum/wizard.py", line 339, in keystore_from_data
    return keystore.from_seed(data['seed'], passphrase=seed_extension, for_multisig=for_multisig)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/vagrant/electrum/electrum/keystore.py", line 1197, in from_seed
    raise BitcoinException('Unexpected seed type {}'.format(repr(t)))
electrum.util.BitcoinException: Unexpected seed type '2fa_segwit'
```

Triggered by the following wizard stack:
```
 30.94 | D | wizard | view=create_seed
 30.94 | D | wizard | resolve_next view is confirm_seed
 30.94 | D | wizard | wizard stack:
0: 0x7fdc6804ae80 - {}

1: 0x7fdc6ac61400 - {'wallet_name': 'wallet_1'}

2: 0x7fdc680d8a80 - {'seed_type': '2fa_segwit', 'wallet_name': 'wallet_1', 'wallet_type': 'multisig'}

3: 0x7fdc6804ab00 - {'multisig_cosigner_data': {}, 'multisig_participants': 2, 'multisig_signatures': 2, 'seed_type': '2fa_segwit', 'wallet_name': 'wallet_1', 'wallet_type': 'multisig'}

4: 0x7fdc6807f0c0 - {'keystore_type': 'createseed', 'multisig_cosigner_data': {}, 'multisig_participants': 2, 'multisig_signatures': 2, 'seed_type': '2fa_segwit', 'wallet_name': 'wallet_1', 'wallet_type': 'multisig'}

c: 0x7fdc6807c380 - {'keystore_type': 'createseed', 'multisig_cosigner_data': {}, 'multisig_participants': 2, 'multisig_signatures': 2, 'seed': '<redacted>', 'seed_extend': False, 'seed_extra_words': '<redacted>', 'seed_type': '2fa_segwit', 'seed_variant': 'electrum', 'wallet_name': 'wallet_1', 'wallet_type': 'multisig'}
 30.94 | W | gui.qml.qeapp | next view: confirm_seed
```
2025-07-09 16:29:20 +02:00
SomberNight
a1ee18f975 qt: crash reporter: replace msg_box with dedi ReportContentsDialog
- the msg_box did not allow neither vertical nor horizontal scrolling
  - long lines were not word-wrapped, but were effectively truncated
  - long reports could only be inspected if the user somehow selected
    the full text and pasted it into a text editor
- new dialog allows vertical and horizontal scrolling
  - we could maybe word-wrap instead of horizontal scrolling,
    but this is already a strict improvement for long reports
2025-07-09 14:24:15 +00:00
f321x
86dd267d15 fix: qml: update tx label on detailsChanged signal
when setting a transaction label in the qml
`LightningPaymentDetails` or `TxDetails` dialogs which get opened
directly by the `ReceiveDialog` (`onRequestPaid`) the label is not applied on the main
transaction list as no callback for `detailsChanged` is registered which
gets called when the label changes. As a result the label is only
visible after the main list gets reloaded (e.g. restart).
This commit adds callbacks for `detailsChanged` to fix this.
2025-07-07 15:55:14 +02:00
ghost43
cd5e6559b6 Merge pull request #9983 from f321x/change_android_qr_lib
android: replace qr code scanning library
2025-07-04 13:02:46 +00:00
Oren
afc62ebb77 Ok to press Sign and not save as a file
It's ok to click the View button, then
press Sign, and then close the window

the signed transaction will be used
by the on_closed callback
2025-07-03 15:50:12 +03:00
Oren
53b3c1de3e control prompt_if_unsaved
In the current logic, even if
prompt_if_unsaved was False,
the prompt will appear if the signing
is complete (and unsaved).
2025-07-03 15:50:12 +03:00
Oren
78c0425931 return tx in on_closed callback 2025-07-03 15:50:12 +03:00
наб
2e68d25187 history: fix displayed STATUS sort order
Ref: #9969
Ref: #9999
2025-07-01 23:33:09 +02:00
SomberNight
38f83940a0 qt gui: history: fix 'txpos_in_block' for mempool/local txs
follow-up https://github.com/spesmilo/electrum/pull/9967
2025-07-01 02:15:21 +00:00
наб
1ae2503014 custom_model: open-code QtCore.QAbstractItemModel.hasIndex()
The implementation is just
	bool QAbstractItemModel::hasIndex(int row, int column, const QModelIndex &parent) const
	{
	    if (row < 0 || column < 0)
	        return false;
	    return row < rowCount(parent) && column < columnCount(parent);
	}
and yet it features as the most prominent part of the profile,
encompassing up to 25% of refresh()

Open-coding it makes refresh() 40% faster. Measurements from between
after wallet.get_full_history() and return from refresh()

Before:
	1.8637257907539606
	1.8563996930606663
	1.7696567592211068
	1.8933695629239082

After:
	1.3133591176010668
	1.3686819169670343
	1.2470976510085166
	1.2455544411204755
2025-07-01 03:32:43 +02:00
наб
5b39e305f6 history_list: if transactions == self.transactions can never hold
We add ['balance'] to all root entries, this never holds even if the
transaction sets themselves are the same
2025-07-01 03:32:43 +02:00
наб
8da694c9c3 history_list: pre-compute sort keys, don't launder getting them through get_data_for_role()
endInsertRows() triggers a sort, the history sort/filter proxy model
then launders the comparison requests through get_data_for_role()
with a custom key, which is then especially looked up in there,
but custom-flattened in the proxy model. This is OO hell

Measuring refresh() from after if transactions == self.transactions: return
to the final return, sorting the wallet from #9958/#6625 takes ~3.5s
and 132348 get_data_for_role() calls

Beside that get_data_for_role() is called maybe 10 times per on-hover?
It's immaterial

Thus: compute the sorting keys when constructing the HistoryNode
and use them directly in the comparison.
This decouples get_data_for_role() from the sorter
and speeds the sort up by ~2x,
now being mostly made up of the python interpreter overhead(?)
for upcalls to lessThan()

A dict wins with a list for the lookup a tiny bit
(and is 100x better code-wise):

1.896097298245877s list
1.840533264912665s list
1.757185084745288s list
1.8990754359401762s list
1.914668960031122s list
1.9349112827330828s list
2.432649422902614s list
1.929884395096451s list
1.9610795709304512s list
1.8694845158606768s list
1.9600030612200499s list
1.9199693519622087s dict
2.0466488380916417s dict
1.8510140180587769s dict
1.8978681536391377s dict
2.0079748439602554s dict
1.9111531740054488s dict
1.9525738609954715s dict
1.850804285146296s dict
1.860573346260935s dict
1.95173170696944s dict
1.8481200002133846s dict

Benchmark 1: dict
  Time (mean ± σ):       1918.039234 ms ±    63.688609 ms
  Range (min … max):     1848.120000 ms …  2046.648838 ms    11 runs

Benchmark 2: list
  Time (mean ± σ):       1945.052027 ms ±   164.019588 ms
  Range (min … max):     1757.185085 ms …  2432.649423 ms    11 runs

Summary
  'dict' ran
    1.014084 ± 0.090082 times faster than 'list'
2025-07-01 03:32:42 +02:00
SomberNight
60bb425ed6 follow-up prev: add explanation 2025-06-30 14:05:21 +00:00
ghost43
1c014a7c85 Merge pull request #9967 from nabijaczleweli/hors-d'oeuvres
Populate tx_status_cache before it's used, not after. Only compute short_id when requested
2025-06-30 13:55:41 +00:00
Sander van Grieken
39c0523773 qml: only save invoice if canSave 2025-06-30 12:56:22 +02:00
SomberNight
10e3bad884 qml: qeinvoice: don't use wallet.get_balance() for NotEnoughFunds checks
related https://github.com/spesmilo/electrum/issues/8835
2025-06-29 22:59:30 +00:00
SomberNight
ba670cc9dc wallet: add struct PiechartBalance 2025-06-29 22:25:56 +00:00
cloudclaim
06d1b730b7 chore: fix some minor issues in the comments
Signed-off-by: cloudclaim <824973921@qq.com>
2025-06-29 10:07:38 +08:00
ghost43
ce64dea0a1 Merge pull request #9990 from f321x/payment_sent_colon
qt: remove colon in "Payment received:" notification
2025-06-27 11:15:14 +00:00
f321x
a50c088c4b qt: remove colon in "Payment received" notification
if there is no amount in the payment request the notification would look
weird as it would just show `Payment received:` indicating something
might be missing. Now it just says `Payment received` instead.
2025-06-27 12:49:12 +02:00
f321x
73249404d4 qt: SwapDialog: update on changes, don't translate fstr
and differenciate between a single 'provider' and multiple or 0
'providers'. Also update SwapDialog and SwapServerDialog
when new offers are incoming. Stops translating f-string.
2025-06-27 11:18:38 +02:00
ghost43
c2b99f08be Merge pull request #9977 from SomberNight/202506_qt_qrtextedit_rm_dupe
qt: qrtextedit: rm some duplication
2025-06-26 13:56:14 +00:00
ThomasV
56684c049a follow-up 2119bf2b23 2025-06-26 14:46:08 +02:00
ThomasV
2119bf2b23 Qt swaps_dialog: update send/recv amounts after changing provider.
Also, show current provider color icon in button
2025-06-26 14:20:12 +02:00
наб
663900270c Only compute short_id when requested 2025-06-25 17:11:32 +02:00
наб
60cb94f845 Populate tx_status_cache before it's used, not after
The endInsertRows() call triggers the main calls to get_data_for_role():
in the current order, the try/except goes to the exception every time

In the updated order, it doesn't do it ever

We have full control over what's in the cache,
and we always fully populate it, so we can get rid of the fallback
2025-06-25 17:11:32 +02:00
f321x
0a05674f2f android: replace qr code scanning library
Replaces the unmaintained and unreliable
`me.dm7.barcodescanner:zxing:1.9.8` qr code scanning library used only
on android with the `com.github.markusfisch:BarcodeScannerView:1.6.0`
(https://github.com/markusfisch/BarcodeScannerView) library which seems
more actively maintained.
The `BarcodeScannerView` library is incredibly fast and scanning qr
codes is now fun again :)

wip: still looking into ways to pin the library.
2025-06-24 17:38:03 +02:00
accumulator
26e6c0900b Merge pull request #9975 from SomberNight/202506_qml_lnurl_btn_disabled
qml: set BtcField.textAsSats initial value to match its text
2025-06-24 12:54:19 +02:00
f321x
f1837ef58d fix: sort qml swap provider list by PoW
This change orders the list of swapproviders in the qml gui by the
`pow_bits` value in the announcement by inserting the orders at the
correct index in the list instead of just appending new incoming offers.
2025-06-24 11:45:34 +02:00
SomberNight
71627b91c4 qml: set BtcField.textAsSats initial value to match its text
textAsSats was being set to 0 initially, regardless of actual text, only healing after onTextChanged.

fixes https://github.com/spesmilo/electrum/issues/9974
2025-06-24 09:19:13 +00:00
SomberNight
5ea614c81a qt: qrtextedit: rm some duplication
There was too much code duplication - there still is a bit...

- in some places buttons had text "Read QR code with camera", in others it was "Read QR code from camera"
- 63c224cb53 added a "on_qr_from_file_input_btn"
  input method, which was not added everywhere.
  - was missing in add_qr_input_combined_button and in editor_contextMenuEvent
2025-06-23 16:10:14 +00:00
SomberNight
6f5041443f qt: SwapServerDialog: explicit columns 2025-06-23 14:49:36 +00:00
ghost43
0c0a8b7c82 Merge pull request #9955 from SomberNight/202506_qt_mac_camera_permission
qt gui: qrreader: macos: add runtime requesting of camera permission
2025-06-23 14:21:52 +00:00
SomberNight
c1d21aab1d qml: QENetwork: reduce log spam a bit in ln-gossip mode 2025-06-23 13:10:44 +00:00
ThomasV
b79a73b483 qt swaps dialog: replace config menu with a direct button 2025-06-21 10:46:33 +02:00