Skip to content

qt: avoid unchanged wallet list rebuilds#10645

Open
rafael81 wants to merge 2 commits into
spesmilo:masterfrom
rafael81:fix-hidden-utxo-refresh-6625
Open

qt: avoid unchanged wallet list rebuilds#10645
rafael81 wants to merge 2 commits into
spesmilo:masterfrom
rafael81:fix-hidden-utxo-refresh-6625

Conversation

@rafael81
Copy link
Copy Markdown

@rafael81 rafael81 commented May 12, 2026

This reduces UI rebuild work related to #6625 in three focused wallet-list paths.

The previous code already deferred many hidden tree-view updates, but large wallets could still pay expensive Qt model rebuild costs in common refresh paths:

  • History tab: visible no-op refreshes still removed and reinserted every history row even when get_full_history() returned the same wallet-derived data already displayed. This branch now keeps the existing model when the history rows are unchanged, while still rebuilding when labels, amounts, confirmations, fiat data, ordering, or row count change. It also fixes the remove/insert row bounds around empty history refreshes and preserves row-0 reselection.
  • Coins tab: UTXOList.update() intentionally skipped maybe_defer_update() so the coin-control status bar stayed accurate. That also meant a hidden Coins tab still cleared and rebuilt every UTXO row. This branch keeps the cheap bookkeeping path current while hidden, marks the view for a deferred repaint, and only rebuilds the Qt model when shown or forced.
  • Addresses tab: visible no-op refreshes still cleared and rebuilt the full address model even when address balances, labels, history counts, filters, fiat state, frozen state, and gap-limit state were unchanged. This branch records that row state before rebuilding and returns early when the displayed model is already current.

This is intentionally scoped as a partial improvement for heavy-wallet freeze paths, not a claim that every case in #6625 is solved.

Validation:

  • .venv/bin/python -m pytest tests/gui/test_qt_history_list.py tests/gui/test_qt_address_list.py tests/gui/test_qt_utxo_list.py tests/qml/test_qml_qeconfig.py tests/test_storage_upgrade.py -q -> 68 passed
  • git diff --check
  • .venv/bin/python -m compileall -q electrum/gui/qt/history_list.py tests/gui/test_qt_history_list.py
  • .venv/bin/python -m compileall -q electrum/gui/qt/address_list.py electrum/gui/qt/utxo_list.py tests/gui/test_qt_address_list.py tests/gui/test_qt_utxo_list.py

Synthetic benchmarks:

  • 5000 fake transactions, visible changed HistoryModel.refresh() median: 0.6553763750707731s
  • 5000 fake transactions, visible unchanged HistoryModel.refresh() median: 0.009829916059970856s
  • 5000 fake UTXOs, hidden UTXOList.update() median: baseline 0.5189501660643145s -> this branch 0.001451167045161128s
  • 5000 fake addresses, unchanged visible AddressList.update() median on this branch: 0.00339225004427135s

This PR was AI-assisted, then manually reviewed and tested with the commands above.

@rafael81 rafael81 force-pushed the fix-hidden-utxo-refresh-6625 branch from 9ca85d0 to 22769c2 Compare May 12, 2026 22:07
@rafael81
Copy link
Copy Markdown
Author

rafael81 commented May 12, 2026

Follow-up pushed: 22769c2.

The first CI run exposed that the new Qt GUI test needed an explicit headless Qt platform when run inside the full Cirrus test suite. I added QT_QPA_PLATFORM=offscreen as a test-local default before importing/creating Qt widgets.

Re-ran locally after the update:

  • .venv/bin/python -m pytest tests/gui/test_qt_utxo_list.py tests/test_storage_upgrade.py -q -> 63 passed
  • git diff --check
  • .venv/bin/python -m compileall -q tests/gui/test_qt_utxo_list.py

Still AI-assisted and manually reviewed/tested.

@rafael81 rafael81 force-pushed the fix-hidden-utxo-refresh-6625 branch from 22769c2 to 105c5bb Compare May 12, 2026 22:44
@rafael81
Copy link
Copy Markdown
Author

rafael81 commented May 12, 2026

Follow-up pushed: 7c5a11f.

This now covers two focused large-wallet list refresh paths:

  • UTXOList: hidden Coins tab updates keep cheap coin-control bookkeeping current, but defer the expensive Qt model rebuild until the view is shown or forced.
  • AddressList: visible no-op refreshes now skip clearing/rebuilding the full Qt model when the displayed address rows are already current. The state check includes address order, balances, labels, history counts, filter mode, fiat visibility/currency/rate, frozen state, and gap-limit highlighting.

Re-ran locally after the update:

  • .venv/bin/python -m pytest tests/gui/test_qt_address_list.py tests/gui/test_qt_utxo_list.py tests/qml/test_qml_qeconfig.py tests/test_storage_upgrade.py -q -> 67 passed
  • git diff --check
  • .venv/bin/python -m compileall -q electrum/gui/qt/address_list.py electrum/gui/qt/utxo_list.py tests/gui/test_qt_address_list.py tests/gui/test_qt_utxo_list.py

Synthetic measurements:

  • hidden UTXOList.update() with 5000 fake UTXOs: baseline median 0.5189501660643145s -> branch median 0.001451167045161128s
  • unchanged visible AddressList.update() with 5000 fake addresses: branch median 0.00339225004427135s

Still intentionally scoped as a partial improvement for #6625, and still AI-assisted / manually reviewed and tested.

CI status after the follow-up push: all required checks are now green on 7c5a11f, including Regtest functional tests.

@rafael81 rafael81 force-pushed the fix-hidden-utxo-refresh-6625 branch from 105c5bb to 7c5a11f Compare May 12, 2026 23:18
@rafael81 rafael81 changed the title qt: defer hidden UTXO list model rebuilds qt: avoid unchanged wallet list rebuilds May 12, 2026
@rafael81
Copy link
Copy Markdown
Author

rafael81 commented May 12, 2026

Follow-up pushed: 33521ca.

This adds the History tab no-op refresh path to the same large-wallet performance scope:

  • visible unchanged HistoryModel.refresh() now keeps the existing model instead of removing/reinserting every history row when get_full_history() returns the same wallet-derived data
  • changed rows still rebuild normally; the equality check includes labels, amounts, confirmations, fiat data, ordering, row count, and the computed running balance
  • fixed the remove/insert row bounds for history refreshes and preserved row-0 reselection

Re-ran locally:

  • .venv/bin/python -m pytest tests/gui/test_qt_history_list.py tests/gui/test_qt_address_list.py tests/gui/test_qt_utxo_list.py tests/qml/test_qml_qeconfig.py tests/test_storage_upgrade.py -q -> 68 passed
  • git diff --check
  • .venv/bin/python -m compileall -q electrum/gui/qt/history_list.py tests/gui/test_qt_history_list.py

Synthetic measurement with 5000 fake transactions:

  • changed visible HistoryModel.refresh() median: 0.6553763750707731s
  • unchanged visible HistoryModel.refresh() median: 0.009829916059970856s

Still scoped as a partial improvement for #6625, and still AI-assisted / manually reviewed and tested.

CI status after the follow-up push: all required checks are now green on 33521ca, including Regtest functional tests.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant