Page MenuHomePhabricator

D14167.id41124.diff
No OneTemporary

D14167.id41124.diff

diff --git a/electrum/electrumabc_gui/qt/address_list.py b/electrum/electrumabc_gui/qt/address_list.py
--- a/electrum/electrumabc_gui/qt/address_list.py
+++ b/electrum/electrumabc_gui/qt/address_list.py
@@ -44,10 +44,10 @@
from .consolidate_coins_dialog import ConsolidateCoinsWizard
from .invoice_dialog import InvoiceDialog
+from .tree_widget import MyTreeWidget
from .util import (
MONOSPACE_FONT,
ColorScheme,
- MyTreeWidget,
SortableTreeWidgetItem,
rate_limited,
webopen,
diff --git a/electrum/electrumabc_gui/qt/contact_list.py b/electrum/electrumabc_gui/qt/contact_list.py
--- a/electrum/electrumabc_gui/qt/contact_list.py
+++ b/electrum/electrumabc_gui/qt/contact_list.py
@@ -41,7 +41,8 @@
from electrumabc.plugins import run_hook
from electrumabc.printerror import PrintError
-from .util import MONOSPACE_FONT, ColorScheme, MyTreeWidget, rate_limited, webopen
+from .tree_widget import MyTreeWidget
+from .util import MONOSPACE_FONT, ColorScheme, rate_limited, webopen
if TYPE_CHECKING:
from .main_window import ElectrumWindow
diff --git a/electrum/electrumabc_gui/qt/history_list.py b/electrum/electrumabc_gui/qt/history_list.py
--- a/electrum/electrumabc_gui/qt/history_list.py
+++ b/electrum/electrumabc_gui/qt/history_list.py
@@ -37,13 +37,8 @@
from electrumabc.plugins import run_hook
from electrumabc.util import Weak, profiler, timestamp_to_datetime
-from .util import (
- MONOSPACE_FONT,
- MyTreeWidget,
- SortableTreeWidgetItem,
- rate_limited,
- webopen,
-)
+from .tree_widget import MyTreeWidget
+from .util import MONOSPACE_FONT, SortableTreeWidgetItem, rate_limited, webopen
if TYPE_CHECKING:
from .main_window import ElectrumWindow
diff --git a/electrum/electrumabc_gui/qt/invoice_list.py b/electrum/electrumabc_gui/qt/invoice_list.py
--- a/electrum/electrumabc_gui/qt/invoice_list.py
+++ b/electrum/electrumabc_gui/qt/invoice_list.py
@@ -33,10 +33,11 @@
from PyQt5.QtGui import QFont, QIcon
from electrumabc.i18n import _
-from electrumabc.paymentrequest import pr_tooltips
+from electrumabc.paymentrequest import PR_UNPAID, pr_tooltips
from electrumabc.util import FileImportFailed, format_time
-from .util import MONOSPACE_FONT, PR_UNPAID, MyTreeWidget, pr_icons
+from .tree_widget import MyTreeWidget, pr_icons
+from .util import MONOSPACE_FONT
if TYPE_CHECKING:
from .main_window import ElectrumWindow
diff --git a/electrum/electrumabc_gui/qt/main_window.py b/electrum/electrumabc_gui/qt/main_window.py
--- a/electrum/electrumabc_gui/qt/main_window.py
+++ b/electrum/electrumabc_gui/qt/main_window.py
@@ -116,6 +116,7 @@
from .sign_verify_dialog import SignVerifyDialog
from .statusbar import NetworkStatus, StatusBar
from .transaction_dialog import show_transaction
+from .tree_widget import MyTreeWidget
from .util import (
MONOSPACE_FONT,
Buttons,
@@ -129,7 +130,6 @@
HelpButton,
HelpLabel,
MessageBoxMixin,
- MyTreeWidget,
OkButton,
RateLimiter,
TaskThread,
diff --git a/electrum/electrumabc_gui/qt/request_list.py b/electrum/electrumabc_gui/qt/request_list.py
--- a/electrum/electrumabc_gui/qt/request_list.py
+++ b/electrum/electrumabc_gui/qt/request_list.py
@@ -37,7 +37,7 @@
from electrumabc.plugins import run_hook
from electrumabc.util import age, format_time
-from .util import MyTreeWidget, pr_icons
+from .tree_widget import MyTreeWidget, pr_icons
if TYPE_CHECKING:
from .main_window import ElectrumWindow
diff --git a/electrum/electrumabc_gui/qt/tree_widget.py b/electrum/electrumabc_gui/qt/tree_widget.py
new file mode 100644
--- /dev/null
+++ b/electrum/electrumabc_gui/qt/tree_widget.py
@@ -0,0 +1,294 @@
+from collections import namedtuple
+
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import Qt, QTimer, pyqtSignal
+
+from electrumabc.paymentrequest import PR_EXPIRED, PR_PAID, PR_UNCONFIRMED, PR_UNPAID
+from electrumabc.simple_config import SimpleConfig
+from electrumabc.util import Weak
+from electrumabc.wallet import AbstractWallet
+
+pr_icons = {
+ PR_UNPAID: ":icons/unpaid.svg",
+ PR_PAID: ":icons/confirmed.svg",
+ PR_EXPIRED: ":icons/expired.svg",
+ PR_UNCONFIRMED: ":icons/unconfirmed.svg",
+}
+
+
+class ElectrumItemDelegate(QtWidgets.QStyledItemDelegate):
+ def createEditor(self, parent, option, index):
+ return self.parent().createEditor(parent, option, index)
+
+
+class MyTreeWidget(QtWidgets.QTreeWidget):
+ class SortSpec(namedtuple("SortSpec", "column, qt_sort_order")):
+ """Used to specify member: default_sort"""
+
+ # Specify this in subclasses to apply a default sort order to the widget.
+ # If None, nothing is applied (items are presented in the order they are
+ # added).
+ default_sort: SortSpec = None
+
+ # Specify this in subclasses to enable substring search/filtering (Ctrl+F)
+ # (if this and filter_data_columns are both empty, no search is applied)
+ filter_columns = []
+ # Like the above but rather than search the item .text() field, it searches
+ # for *data* in columns, e.g. item.data(col, Qt.UserRole). The data must
+ # live in filter_data_role (Qt.UserRole by default) in the specified
+ # column(s) and be a str. Leave empty to disable this facility. Note that
+ # data matches for the Ctrl+F filter must be a full string match (no
+ # substring matching is done) -- this is in contrast to filter_columns
+ # matching above which does substring matching. This reason we match on full
+ # strings is this facility was initially added to allow for searching the
+ # history_list by txid. If this criterion doesn't suit your use-case when
+ # inheriting from this, you may always override this class's `filter`
+ # method.
+ filter_data_columns = []
+ # the QTreeWidgetItem data role to use when searching data columns
+ filter_data_role: int = Qt.UserRole
+
+ edited = pyqtSignal()
+
+ def __init__(
+ self,
+ parent: QtWidgets.QWidget,
+ headers,
+ config: SimpleConfig,
+ wallet: AbstractWallet,
+ stretch_column=None,
+ editable_columns=None,
+ *,
+ deferred_updates=False,
+ save_sort_settings=False,
+ ):
+ QtWidgets.QTreeWidget.__init__(self, parent)
+ self.config = config
+ self.wallet = wallet
+ self.stretch_column = stretch_column
+ self.setContextMenuPolicy(Qt.CustomContextMenu)
+ self.setUniformRowHeights(True)
+ # extend the syntax for consistency
+ self.addChild = self.addTopLevelItem
+ self.insertChild = self.insertTopLevelItem
+ self.deferred_updates = deferred_updates
+ self.deferred_update_ct, self._forced_update = 0, False
+ self._save_sort_settings = save_sort_settings
+
+ # Control which columns are editable
+ self.editor = None
+ self.pending_update = False
+ if editable_columns is None:
+ editable_columns = [stretch_column]
+ self.editable_columns = editable_columns
+ self.setItemDelegate(ElectrumItemDelegate(self))
+ self.itemDoubleClicked.connect(self.on_doubleclick)
+ self.update_headers(headers)
+ self.current_filter = ""
+
+ self._setup_save_sort_mechanism()
+
+ def _setup_save_sort_mechanism(self):
+ if self._save_sort_settings:
+ storage = self.wallet.storage
+ key = f"mytreewidget_default_sort_{type(self).__name__}"
+ default = (storage and storage.get(key, None)) or self.default_sort
+ if (
+ default
+ and isinstance(default, (tuple, list))
+ and len(default) >= 2
+ and all(isinstance(i, int) for i in default)
+ ):
+ self.setSortingEnabled(True)
+ self.sortByColumn(default[0], default[1])
+ if storage:
+ # Paranoia; hold a weak reference just in case subclass code
+ # does unusual things.
+ weakStorage = Weak.ref(storage)
+
+ def save_sort(column, qt_sort_order):
+ storage = weakStorage()
+ if storage:
+ storage.put(key, [column, qt_sort_order])
+
+ self.header().sortIndicatorChanged.connect(save_sort)
+ elif self.default_sort:
+ self.setSortingEnabled(True)
+ self.sortByColumn(self.default_sort[0], self.default_sort[1])
+
+ def update_headers(self, headers):
+ self.setColumnCount(len(headers))
+ self.setHeaderLabels(headers)
+ self.header().setStretchLastSection(False)
+ for col in range(len(headers)):
+ sm = (
+ QtWidgets.QHeaderView.Stretch
+ if col == self.stretch_column
+ else QtWidgets.QHeaderView.ResizeToContents
+ )
+ self.header().setSectionResizeMode(col, sm)
+
+ def editItem(self, item, column):
+ if item and column in self.editable_columns:
+ self.editing_itemcol = (item, column, item.text(column))
+ # Calling setFlags causes on_changed events for some reason
+ item.setFlags(item.flags() | Qt.ItemIsEditable)
+ super().editItem(item, column)
+ item.setFlags(item.flags() & ~Qt.ItemIsEditable)
+
+ def keyPressEvent(self, event):
+ if event.key() in {Qt.Key_F2, Qt.Key_Return} and self.editor is None:
+ item, col = self.currentItem(), self.currentColumn()
+ if item and col > -1:
+ self.on_activated(item, col)
+ else:
+ super().keyPressEvent(event)
+
+ def permit_edit(self, item, column):
+ return column in self.editable_columns and self.on_permit_edit(item, column)
+
+ def on_permit_edit(self, item, column):
+ return True
+
+ def on_doubleclick(self, item, column):
+ if self.permit_edit(item, column):
+ self.editItem(item, column)
+
+ def on_activated(self, item, column):
+ # on 'enter' we show the menu
+ pt = self.visualItemRect(item).bottomLeft()
+ pt.setX(50)
+ self.customContextMenuRequested.emit(pt)
+
+ def createEditor(self, parent, option, index):
+ self.editor = QtWidgets.QStyledItemDelegate.createEditor(
+ self.itemDelegate(), parent, option, index
+ )
+ self.editor.editingFinished.connect(self.editing_finished)
+ return self.editor
+
+ def editing_finished(self):
+ # Long-time QT bug - pressing Enter to finish editing signals
+ # editingFinished twice. If the item changed the sequence is
+ # Enter key: editingFinished, on_change, editingFinished
+ # Mouse: on_change, editingFinished
+ # This mess is the cleanest way to ensure we make the
+ # on_edited callback with the updated item
+ if self.editor:
+ (item, column, prior_text) = self.editing_itemcol
+ if self.editor.text() == prior_text:
+ self.editor = None # Unchanged - ignore any 2nd call
+ elif item.text(column) == prior_text:
+ pass # Buggy first call on Enter key, item not yet updated
+ else:
+ # What we want - the updated item
+ self.on_edited(*self.editing_itemcol)
+ self.editor = None
+
+ # Now do any pending updates
+ if self.editor is None and self.pending_update:
+ self.pending_update = False
+ self.on_update()
+ self.deferred_update_ct = 0
+
+ def on_edited(self, item, column, prior):
+ """Called only when the text actually changes"""
+ key = item.data(0, Qt.UserRole)
+ text = item.text(column)
+ self.wallet.set_label(key, text)
+ self.edited.emit()
+
+ def should_defer_update_incr(self):
+ ret = self.deferred_updates and not self.isVisible() and not self._forced_update
+ if ret:
+ self.deferred_update_ct += 1
+ return ret
+
+ def update(self):
+ # Defer updates if editing
+ if self.editor:
+ self.pending_update = True
+ else:
+ # Deferred update mode won't actually update the GUI if it's
+ # not on-screen, and will instead update it the next time it is
+ # shown. This has been found to radically speed up large wallets
+ # on initial synch or when new TX's arrive.
+ if self.should_defer_update_incr():
+ return
+ self.setUpdatesEnabled(False)
+ scroll_pos_val = (
+ self.verticalScrollBar().value()
+ ) # save previous scroll bar position
+ self.on_update()
+ self.deferred_update_ct = 0
+ weakSelf = Weak.ref(self)
+
+ def restoreScrollBar():
+ slf = weakSelf()
+ if slf:
+ slf.updateGeometry()
+ slf.verticalScrollBar().setValue(
+ scroll_pos_val
+ ) # restore scroll bar to previous
+ slf.setUpdatesEnabled(True)
+
+ QTimer.singleShot(
+ 0, restoreScrollBar
+ ) # need to do this from a timer some time later due to Qt quirks
+ if self.current_filter:
+ self.filter(self.current_filter)
+
+ def on_update(self):
+ # Reimplemented in subclasses
+ pass
+
+ def showEvent(self, e):
+ super().showEvent(e)
+ if e.isAccepted() and self.deferred_update_ct:
+ self._forced_update = True
+ self.update()
+ self._forced_update = False
+ # self.deferred_update_ct will be set right after on_update is called because some subclasses use @rate_limiter on the update() method
+
+ def get_leaves(self, root=None):
+ if root is None:
+ root = self.invisibleRootItem()
+ child_count = root.childCount()
+ if child_count == 0:
+ if root is not self.invisibleRootItem():
+ yield root
+ else:
+ return
+ for i in range(child_count):
+ item = root.child(i)
+ for x in self.get_leaves(item):
+ yield x
+
+ def filter(self, p):
+ columns = self.__class__.filter_columns
+ data_columns = self.__class__.filter_data_columns
+ if not columns and not data_columns:
+ return
+ p = p.lower()
+ self.current_filter = p
+ bad_data_column = False
+ data_role = self.__class__.filter_data_role
+ for item in self.get_leaves(self.invisibleRootItem()):
+ no_match_text = all(
+ item.text(column).lower().find(p) == -1 for column in columns
+ )
+ no_match_data = True
+ if no_match_text and not bad_data_column and data_columns:
+ try:
+ # data matching is different -- it must match exactly the
+ # specified search string. This was originally designed
+ # to allow for tx-hash searching of the history list.
+ no_match_data = all(
+ item.data(column, data_role).strip().lower() != p
+ for column in data_columns
+ )
+ except (AttributeError, TypeError, ValueError):
+ # flag so we don't keep raising for each iteration of this
+ # loop. Programmer error here in subclass, silently ignore.
+ bad_data_column = True
+ item.setHidden(no_match_text and no_match_data)
diff --git a/electrum/electrumabc_gui/qt/util.py b/electrum/electrumabc_gui/qt/util.py
--- a/electrum/electrumabc_gui/qt/util.py
+++ b/electrum/electrumabc_gui/qt/util.py
@@ -25,11 +25,8 @@
QPixmap,
)
-from electrumabc.paymentrequest import PR_EXPIRED, PR_PAID, PR_UNCONFIRMED, PR_UNPAID
from electrumabc.printerror import PrintError, print_error
-from electrumabc.simple_config import SimpleConfig
from electrumabc.util import Weak, finalization_print_error
-from electrumabc.wallet import AbstractWallet
if platform.system() == "Windows":
MONOSPACE_FONT = "Consolas"
@@ -41,13 +38,6 @@
dialogs = []
-pr_icons = {
- PR_UNPAID: ":icons/unpaid.svg",
- PR_PAID: ":icons/confirmed.svg",
- PR_EXPIRED: ":icons/expired.svg",
- PR_UNCONFIRMED: ":icons/unconfirmed.svg",
-}
-
# https://docs.python.org/3/library/gettext.html#deferred-translations
def _(message):
@@ -640,284 +630,6 @@
return gb, filename_e, b1
-class ElectrumItemDelegate(QtWidgets.QStyledItemDelegate):
- def createEditor(self, parent, option, index):
- return self.parent().createEditor(parent, option, index)
-
-
-class MyTreeWidget(QtWidgets.QTreeWidget):
- class SortSpec(namedtuple("SortSpec", "column, qt_sort_order")):
- """Used to specify member: default_sort"""
-
- # Specify this in subclasses to apply a default sort order to the widget.
- # If None, nothing is applied (items are presented in the order they are
- # added).
- default_sort: SortSpec = None
-
- # Specify this in subclasses to enable substring search/filtering (Ctrl+F)
- # (if this and filter_data_columns are both empty, no search is applied)
- filter_columns = []
- # Like the above but rather than search the item .text() field, it searches
- # for *data* in columns, e.g. item.data(col, Qt.UserRole). The data must
- # live in filter_data_role (Qt.UserRole by default) in the specified
- # column(s) and be a str. Leave empty to disable this facility. Note that
- # data matches for the Ctrl+F filter must be a full string match (no
- # substring matching is done) -- this is in contrast to filter_columns
- # matching above which does substring matching. This reason we match on full
- # strings is this facility was initially added to allow for searching the
- # history_list by txid. If this criterion doesn't suit your use-case when
- # inheriting from this, you may always override this class's `filter`
- # method.
- filter_data_columns = []
- # the QTreeWidgetItem data role to use when searching data columns
- filter_data_role: int = Qt.UserRole
-
- edited = pyqtSignal()
-
- def __init__(
- self,
- parent: QtWidgets.QWidget,
- headers,
- config: SimpleConfig,
- wallet: AbstractWallet,
- stretch_column=None,
- editable_columns=None,
- *,
- deferred_updates=False,
- save_sort_settings=False,
- ):
- QtWidgets.QTreeWidget.__init__(self, parent)
- self.config = config
- self.wallet = wallet
- self.stretch_column = stretch_column
- self.setContextMenuPolicy(Qt.CustomContextMenu)
- self.setUniformRowHeights(True)
- # extend the syntax for consistency
- self.addChild = self.addTopLevelItem
- self.insertChild = self.insertTopLevelItem
- self.deferred_updates = deferred_updates
- self.deferred_update_ct, self._forced_update = 0, False
- self._save_sort_settings = save_sort_settings
-
- # Control which columns are editable
- self.editor = None
- self.pending_update = False
- if editable_columns is None:
- editable_columns = [stretch_column]
- self.editable_columns = editable_columns
- self.setItemDelegate(ElectrumItemDelegate(self))
- self.itemDoubleClicked.connect(self.on_doubleclick)
- self.update_headers(headers)
- self.current_filter = ""
-
- self._setup_save_sort_mechanism()
-
- def _setup_save_sort_mechanism(self):
- if self._save_sort_settings:
- storage = self.wallet.storage
- key = f"mytreewidget_default_sort_{type(self).__name__}"
- default = (storage and storage.get(key, None)) or self.default_sort
- if (
- default
- and isinstance(default, (tuple, list))
- and len(default) >= 2
- and all(isinstance(i, int) for i in default)
- ):
- self.setSortingEnabled(True)
- self.sortByColumn(default[0], default[1])
- if storage:
- # Paranoia; hold a weak reference just in case subclass code
- # does unusual things.
- weakStorage = Weak.ref(storage)
-
- def save_sort(column, qt_sort_order):
- storage = weakStorage()
- if storage:
- storage.put(key, [column, qt_sort_order])
-
- self.header().sortIndicatorChanged.connect(save_sort)
- elif self.default_sort:
- self.setSortingEnabled(True)
- self.sortByColumn(self.default_sort[0], self.default_sort[1])
-
- def update_headers(self, headers):
- self.setColumnCount(len(headers))
- self.setHeaderLabels(headers)
- self.header().setStretchLastSection(False)
- for col in range(len(headers)):
- sm = (
- QtWidgets.QHeaderView.Stretch
- if col == self.stretch_column
- else QtWidgets.QHeaderView.ResizeToContents
- )
- self.header().setSectionResizeMode(col, sm)
-
- def editItem(self, item, column):
- if item and column in self.editable_columns:
- self.editing_itemcol = (item, column, item.text(column))
- # Calling setFlags causes on_changed events for some reason
- item.setFlags(item.flags() | Qt.ItemIsEditable)
- super().editItem(item, column)
- item.setFlags(item.flags() & ~Qt.ItemIsEditable)
-
- def keyPressEvent(self, event):
- if event.key() in {Qt.Key_F2, Qt.Key_Return} and self.editor is None:
- item, col = self.currentItem(), self.currentColumn()
- if item and col > -1:
- self.on_activated(item, col)
- else:
- super().keyPressEvent(event)
-
- def permit_edit(self, item, column):
- return column in self.editable_columns and self.on_permit_edit(item, column)
-
- def on_permit_edit(self, item, column):
- return True
-
- def on_doubleclick(self, item, column):
- if self.permit_edit(item, column):
- self.editItem(item, column)
-
- def on_activated(self, item, column):
- # on 'enter' we show the menu
- pt = self.visualItemRect(item).bottomLeft()
- pt.setX(50)
- self.customContextMenuRequested.emit(pt)
-
- def createEditor(self, parent, option, index):
- self.editor = QtWidgets.QStyledItemDelegate.createEditor(
- self.itemDelegate(), parent, option, index
- )
- self.editor.editingFinished.connect(self.editing_finished)
- return self.editor
-
- def editing_finished(self):
- # Long-time QT bug - pressing Enter to finish editing signals
- # editingFinished twice. If the item changed the sequence is
- # Enter key: editingFinished, on_change, editingFinished
- # Mouse: on_change, editingFinished
- # This mess is the cleanest way to ensure we make the
- # on_edited callback with the updated item
- if self.editor:
- (item, column, prior_text) = self.editing_itemcol
- if self.editor.text() == prior_text:
- self.editor = None # Unchanged - ignore any 2nd call
- elif item.text(column) == prior_text:
- pass # Buggy first call on Enter key, item not yet updated
- else:
- # What we want - the updated item
- self.on_edited(*self.editing_itemcol)
- self.editor = None
-
- # Now do any pending updates
- if self.editor is None and self.pending_update:
- self.pending_update = False
- self.on_update()
- self.deferred_update_ct = 0
-
- def on_edited(self, item, column, prior):
- """Called only when the text actually changes"""
- key = item.data(0, Qt.UserRole)
- text = item.text(column)
- self.wallet.set_label(key, text)
- self.edited.emit()
-
- def should_defer_update_incr(self):
- ret = self.deferred_updates and not self.isVisible() and not self._forced_update
- if ret:
- self.deferred_update_ct += 1
- return ret
-
- def update(self):
- # Defer updates if editing
- if self.editor:
- self.pending_update = True
- else:
- # Deferred update mode won't actually update the GUI if it's
- # not on-screen, and will instead update it the next time it is
- # shown. This has been found to radically speed up large wallets
- # on initial synch or when new TX's arrive.
- if self.should_defer_update_incr():
- return
- self.setUpdatesEnabled(False)
- scroll_pos_val = (
- self.verticalScrollBar().value()
- ) # save previous scroll bar position
- self.on_update()
- self.deferred_update_ct = 0
- weakSelf = Weak.ref(self)
-
- def restoreScrollBar():
- slf = weakSelf()
- if slf:
- slf.updateGeometry()
- slf.verticalScrollBar().setValue(
- scroll_pos_val
- ) # restore scroll bar to previous
- slf.setUpdatesEnabled(True)
-
- QTimer.singleShot(
- 0, restoreScrollBar
- ) # need to do this from a timer some time later due to Qt quirks
- if self.current_filter:
- self.filter(self.current_filter)
-
- def on_update(self):
- # Reimplemented in subclasses
- pass
-
- def showEvent(self, e):
- super().showEvent(e)
- if e.isAccepted() and self.deferred_update_ct:
- self._forced_update = True
- self.update()
- self._forced_update = False
- # self.deferred_update_ct will be set right after on_update is called because some subclasses use @rate_limiter on the update() method
-
- def get_leaves(self, root=None):
- if root is None:
- root = self.invisibleRootItem()
- child_count = root.childCount()
- if child_count == 0:
- if root is not self.invisibleRootItem():
- yield root
- else:
- return
- for i in range(child_count):
- item = root.child(i)
- for x in self.get_leaves(item):
- yield x
-
- def filter(self, p):
- columns = self.__class__.filter_columns
- data_columns = self.__class__.filter_data_columns
- if not columns and not data_columns:
- return
- p = p.lower()
- self.current_filter = p
- bad_data_column = False
- data_role = self.__class__.filter_data_role
- for item in self.get_leaves(self.invisibleRootItem()):
- no_match_text = all(
- item.text(column).lower().find(p) == -1 for column in columns
- )
- no_match_data = True
- if no_match_text and not bad_data_column and data_columns:
- try:
- # data matching is different -- it must match exactly the
- # specified search string. This was originally designed
- # to allow for tx-hash searching of the history list.
- no_match_data = all(
- item.data(column, data_role).strip().lower() != p
- for column in data_columns
- )
- except (AttributeError, TypeError, ValueError):
- # flag so we don't keep raising for each iteration of this
- # loop. Programmer error here in subclass, silently ignore.
- bad_data_column = True
- item.setHidden(no_match_text and no_match_data)
-
-
class OverlayControlMixin:
STYLE_SHEET_COMMON = """
QPushButton { border-width: 1px; padding: 0px; margin: 0px; }
diff --git a/electrum/electrumabc_gui/qt/utxo_list.py b/electrum/electrumabc_gui/qt/utxo_list.py
--- a/electrum/electrumabc_gui/qt/utxo_list.py
+++ b/electrum/electrumabc_gui/qt/utxo_list.py
@@ -44,13 +44,8 @@
from .avalanche.proof_editor import AvaProofDialog
from .consolidate_coins_dialog import ConsolidateCoinsWizard
-from .util import (
- MONOSPACE_FONT,
- ColorScheme,
- MyTreeWidget,
- SortableTreeWidgetItem,
- rate_limited,
-)
+from .tree_widget import MyTreeWidget
+from .util import MONOSPACE_FONT, ColorScheme, SortableTreeWidgetItem, rate_limited
if TYPE_CHECKING:
from .main_window import ElectrumWindow

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 26, 10:34 (5 h, 23 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
5573277
Default Alt Text
D14167.id41124.diff (28 KB)

Event Timeline