Commit Graph

2206 Commits

Author SHA1 Message Date
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 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
f321x bb5b1b3932 wizard: move pasphrase flow from Qt into Abstract Wizard
moves the separate passphrase flow logic from Qt into the Abstract
Wizard base class so the same flow can be shared between Qt and QML
2025-07-11 16:39:31 +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
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
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
SomberNight ba670cc9dc wallet: add struct PiechartBalance 2025-06-29 22:25:56 +00: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
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"
- https://github.com/spesmilo/electrum/commit/63c224cb53739c5b4b5630e3a0bfb8208d785879 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
ThomasV b79a73b483 qt swaps dialog: replace config menu with a direct button 2025-06-21 10:46:33 +02:00
ThomasV 2607a0d9f6 Plugins dialog: remove direct download option.
Since users have to trust plugin publishers, we should expect
the plugin to be signed by its author, and the user to verify
the signature before installing.
2025-06-21 10:10:31 +02:00
ThomasV d7f6820fd6 hardware_wallets: register keystore if if external plugin was authorized
Also remove 'gui_good' from hw_wallets dict (was always True)
2025-06-19 11:23:57 +02:00
f321x c58ff7caec qt: wrap show_bitcoin_paper() in WaitingDialog
i noticed that the `show_bitcoin_paper()` call can be a bit slow on some
machines blocking the gui for multiple seconds without giving feedback.
Wrapping it in a waiting dialog gives the user some feedback that
something is happening.
2025-06-16 10:08:11 +02:00
SomberNight 612d82e8d4 qt gui: qrreader: macos: add runtime requesting of camera permission
- we were already
  - statically declaring "NSCameraUsageDescription" in the Info.plist
    - this used to be enough in the past
  - codesigning with an entitlements.plist that declares "com.apple.security.device.camera"
    - I believe this is required for notarization to pass for an app that declares "NSCameraUsageDescription".
- previously this was enough to access the camera IIRC
  - in any case, if the user goes into "System Preferences">"Security & Privacy", they can manually modify permissions there
- now with this commit, we on-demand trigger at runtime the OS permission prompt
  - making it much easier for users to actually use the camera
  - note: if you run via the Terminal, e.g. `$ $HOME/Desktop/Electrum.app/Contents/MacOS/run_electrum`,
    then it will be the Terminal app that requires the camera permission. If you run by double-clicking Electrum.app,
    then Electrum.app will be the "app" that requires the camera permission.
  - `$ tccutil reset Camera` can be used to clear permissions for all apps (back to the Qt::PermissionStatus::Undetermined state)

ref https://doc.qt.io/qt-6.5/qcoreapplication.html#requestPermission-1
2025-06-14 17:06:15 +00:00
SomberNight f366f3e7fb qt gui: be more resilient against import issues of QtMultimedia
The user should still be able to open the Preferences dialog, despite the camera functionality being broken.

see https://github.com/spesmilo/electrum/issues/9949

```
  6.36 | E | gui.qt.exception_window.Exception_Hook | exception caught by crash reporter
Traceback (most recent call last):
  File "electrum\gui\qt\main_window.py", line 2672, in settings_dialog
  File "electrum\gui\qt\settings_dialog.py", line 229, in __init__
  File "electrum\gui\qt\qrreader\__init__.py", line 96, in find_system_cameras
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "PyInstaller\loader\pyimod02_importers.py", line 450, in exec_module
  File "electrum\gui\qt\qrreader\qtmultimedia\__init__.py", line 28, in <module>
  File "<frozen importlib._bootstrap>", line 1360, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1331, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 935, in _load_unlocked
  File "PyInstaller\loader\pyimod02_importers.py", line 450, in exec_module
  File "electrum\gui\qt\qrreader\qtmultimedia\camera_dialog.py", line 32, in <module>
RuntimeError: PyQt6.QtMultimedia cannot import type '���d�⌂' from PyQt6.QtCore

```
2025-06-13 14:22:31 +00:00
ThomasV 40e244dfe9 Merge pull request #9948 from f321x/disable_disabling_i_agree_ToU
qt: fix: ToU 'I Accept' button not getting enabled
2025-06-13 15:06:06 +02:00
f321x 52e2ced62e qt: fix: ToU 'I Accept' button not getting enabled
The 'I Accept' button might not get enabled for some users where startup
is very slow.
The first check if the 'I Agree' button should be enabled gets fired
100ms after constructing the Dialog, which might not be enough.
So we can either remove the logic completely (done here) to prevent these
issues, or alternatively increase the initial timer to some large timout
after which the window should be assembled fully (e.g. 2 seconds). As
the user is not able to read the whole terms in few seconds this would
be a viable option too.
2025-06-13 14:45:27 +02:00
f321x cc848be61c qt: fix: set ApplicationAttribute.AA_DontShowIconsInMenus False
Explicitly sets the AA_DontShowIconsInMenus attribute to false as it
defaults to true on some Qt versions / environments which causes icons
to not show up in the menus (e.g. MacOS).
see https://forum.qt.io/post/813228
The attribute has to be set on self.app after the QApplication has been
instanciated (https://doc.qt.io/qt-6/qt.html#ApplicationAttribute-enum)
2025-06-13 12:21:57 +02:00
f321x 97f9521cb1 fix: handle ! amount for payment identifiers
fixes exceptions ocurring when entering a '!' amount with a payment
identifier that is not allowed to send max (e.g. bolt11).
fixes exception when "Max" is still set from previous PI and the PI gets
replaced by one that doesn't allow max sending.
2025-06-11 19:26:26 +02:00
f321x 86183797db fix: update do_export_history to use wallet.get_full_history
`do_export_history()` which is used by the qt history export function was
broken as it used a method that did not exist anymore.
this updates `do_export_history()` to use `get_full_history()` and also
adds support for payment grouping and lightning transactions to the
generated CSV file.
2025-06-11 11:03:57 +02:00
ghost43 e30392cd16 Merge pull request #9934 from f321x/matplotlib_warning
qt: provide more detailed warning for plot feature, change matplotlib backend
2025-06-10 18:41:42 +00:00
SomberNight 47e76b25b1 swaps: add some type hints, and force kwargs 2025-06-10 16:20:43 +00:00
f321x 229219da9c qt: provide clearer warning for plot feature
this warning is more detailed and explains the user why the plotting
feature is not available to prevent confusion.
2025-06-10 16:03:30 +02:00
ThomasV 660b97c906 follow-up prev: show the plugin dialog 2025-06-10 11:32:39 +02:00
f321x df66126389 disable "Enable" button of unavailable plugins
this changes PluginsDialog to disable the "Enable" button of plugins
that are missing dependencies (e.g. amodem) instead of not showing them
at all. A tooltip is shown explaining the user why the plugin is
disabled.
2025-06-10 11:19:55 +02:00
ThomasV 630124136e plugins dialog: fix is_available, do not show plugins that are not available 2025-06-10 11:02:18 +02:00
ThomasV 15e77d2d80 Merge pull request #9929 from f321x/swapserver_icon
qt: show deterministic colors for swapservers
2025-06-10 09:57:36 +02:00
ThomasV e6ba90f219 Merge pull request #9928 from f321x/swap_fee_slider
swaps: use eta:2 instead of config fee policy in qml, disable qt fee slider on reverse swaps
2025-06-10 09:51:00 +02:00
f321x 6af54866c9 qt: show deterministic colors for swapservers
shows a little color icon which generated deterministically from
the server pubkey for easier differentiation.
2025-06-10 09:49:03 +02:00