QAbstractItemModel: fix sorting, QAbstractItemDelegate usage, QVariant usage
This commit is contained in:
@@ -49,12 +49,8 @@ class ContactList(MyTreeView):
|
|||||||
|
|
||||||
def on_edited(self, idx, user_role, text):
|
def on_edited(self, idx, user_role, text):
|
||||||
_type, prior_name = self.parent.contacts.pop(user_role)
|
_type, prior_name = self.parent.contacts.pop(user_role)
|
||||||
|
self.parent.set_contact(text, user_role)
|
||||||
# TODO when min Qt >= 5.11, use siblingAtColumn
|
self.update()
|
||||||
col_1_sibling = idx.sibling(idx.row(), 1)
|
|
||||||
col_1_item = self.model().itemFromIndex(col_1_sibling)
|
|
||||||
|
|
||||||
self.parent.set_contact(text, col_1_item.text())
|
|
||||||
|
|
||||||
def import_contacts(self):
|
def import_contacts(self):
|
||||||
import_meta_gui(self.parent, _('contacts'), self.parent.contacts.import_file, self.update)
|
import_meta_gui(self.parent, _('contacts'), self.parent.contacts.import_file, self.update)
|
||||||
|
|||||||
@@ -67,9 +67,6 @@ class HistorySortModel(QSortFilterProxyModel):
|
|||||||
return False
|
return False
|
||||||
return item1.value() < item2.value()
|
return item1.value() < item2.value()
|
||||||
|
|
||||||
# requires PyQt5 5.11
|
|
||||||
indexIsValid = QAbstractItemModel.CheckIndexOptions(QAbstractItemModel.CheckIndexOption.IndexIsValid.value)
|
|
||||||
|
|
||||||
class HistoryModel(QAbstractItemModel):
|
class HistoryModel(QAbstractItemModel):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
@@ -80,14 +77,15 @@ class HistoryModel(QAbstractItemModel):
|
|||||||
return 8
|
return 8
|
||||||
|
|
||||||
def rowCount(self, parent: QModelIndex):
|
def rowCount(self, parent: QModelIndex):
|
||||||
l = len(self.transactions)
|
return len(self.transactions)
|
||||||
return l
|
|
||||||
|
|
||||||
def index(self, row: int, column: int, parent : QModelIndex):
|
def index(self, row: int, column: int, parent : QModelIndex):
|
||||||
return self.createIndex(row,column)
|
return self.createIndex(row,column)
|
||||||
|
|
||||||
def data(self, index: QModelIndex, role: Qt.ItemDataRole):
|
def data(self, index: QModelIndex, role: Qt.ItemDataRole):
|
||||||
assert self.checkIndex(index, indexIsValid)
|
# requires PyQt5 5.11
|
||||||
|
# indexIsValid = QAbstractItemModel.CheckIndexOptions(QAbstractItemModel.CheckIndexOption.IndexIsValid.value)
|
||||||
|
# assert self.checkIndex(index, indexIsValid)
|
||||||
assert index.isValid()
|
assert index.isValid()
|
||||||
tx_item = self.transactions[index.row()]
|
tx_item = self.transactions[index.row()]
|
||||||
tx_hash = tx_item['txid']
|
tx_hash = tx_item['txid']
|
||||||
@@ -169,6 +167,10 @@ class HistoryModel(QAbstractItemModel):
|
|||||||
return self.parent.wallet.get_addresses()
|
return self.parent.wallet.get_addresses()
|
||||||
|
|
||||||
def refresh(self, reason: str):
|
def refresh(self, reason: str):
|
||||||
|
selected = self.parent.history_list.selectionModel().currentIndex()
|
||||||
|
selected_row = None
|
||||||
|
if selected:
|
||||||
|
selected_row = selected.row()
|
||||||
fx = self.parent.fx
|
fx = self.parent.fx
|
||||||
if fx: fx.history_used_spot = False
|
if fx: fx.history_used_spot = False
|
||||||
r = self.parent.wallet.get_full_history(domain=self.get_domain(), from_timestamp=None, to_timestamp=None, fx=fx)
|
r = self.parent.wallet.get_full_history(domain=self.get_domain(), from_timestamp=None, to_timestamp=None, fx=fx)
|
||||||
@@ -182,6 +184,8 @@ class HistoryModel(QAbstractItemModel):
|
|||||||
self.beginInsertRows(QModelIndex(), 0, len(r['transactions'])-1)
|
self.beginInsertRows(QModelIndex(), 0, len(r['transactions'])-1)
|
||||||
self.transactions = r['transactions']
|
self.transactions = r['transactions']
|
||||||
self.endInsertRows()
|
self.endInsertRows()
|
||||||
|
if selected_row:
|
||||||
|
self.parent.history_list.selectionModel().select(self.createIndex(selected_row, 0), QItemSelectionModel.Rows | QItemSelectionModel.SelectCurrent)
|
||||||
f = self.parent.history_list.current_filter
|
f = self.parent.history_list.current_filter
|
||||||
if f:
|
if f:
|
||||||
self.parent.history_list.filter(f)
|
self.parent.history_list.filter(f)
|
||||||
@@ -279,7 +283,6 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
|
|||||||
self.proxy.setSourceModel(model)
|
self.proxy.setSourceModel(model)
|
||||||
self.setModel(self.proxy)
|
self.setModel(self.proxy)
|
||||||
|
|
||||||
self.summary = {}
|
|
||||||
self.config = parent.config
|
self.config = parent.config
|
||||||
AcceptFileDragDrop.__init__(self, ".txn")
|
AcceptFileDragDrop.__init__(self, ".txn")
|
||||||
self.setSortingEnabled(True)
|
self.setSortingEnabled(True)
|
||||||
@@ -374,7 +377,7 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
|
|||||||
return datetime.datetime(date.year, date.month, date.day)
|
return datetime.datetime(date.year, date.month, date.day)
|
||||||
|
|
||||||
def show_summary(self):
|
def show_summary(self):
|
||||||
h = self.summary
|
h = self.model().sourceModel().summary
|
||||||
if not h:
|
if not h:
|
||||||
self.parent.show_message(_("Nothing to summarize."))
|
self.parent.show_message(_("Nothing to summarize."))
|
||||||
return
|
return
|
||||||
@@ -425,7 +428,7 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
|
|||||||
self.parent.show_message(str(e))
|
self.parent.show_message(str(e))
|
||||||
|
|
||||||
def on_edited(self, index, user_role, text):
|
def on_edited(self, index, user_role, text):
|
||||||
print("on_edited")
|
index = self.model().mapToSource(index)
|
||||||
row, column = index.row(), index.column()
|
row, column = index.row(), index.column()
|
||||||
tx_item = self.hm.transactions[row]
|
tx_item = self.hm.transactions[row]
|
||||||
key = tx_item['txid']
|
key = tx_item['txid']
|
||||||
@@ -438,7 +441,7 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
|
|||||||
self.wallet.set_fiat_value(key, self.parent.fx.ccy, text, self.parent.fx, tx_item['value'].value)
|
self.wallet.set_fiat_value(key, self.parent.fx.ccy, text, self.parent.fx, tx_item['value'].value)
|
||||||
value = tx_item['value'].value
|
value = tx_item['value'].value
|
||||||
if value is not None:
|
if value is not None:
|
||||||
self.hm.update_fiat(row, self.model().mapToSource(index))
|
self.hm.update_fiat(row, index)
|
||||||
else:
|
else:
|
||||||
assert False
|
assert False
|
||||||
|
|
||||||
@@ -472,7 +475,7 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
|
|||||||
column_data = tx_item['txid']
|
column_data = tx_item['txid']
|
||||||
else:
|
else:
|
||||||
column_title = self.hm.headerData(column, Qt.Horizontal, Qt.DisplayRole)
|
column_title = self.hm.headerData(column, Qt.Horizontal, Qt.DisplayRole)
|
||||||
column_data = str(self.hm.data(idx, Qt.DisplayRole))
|
column_data = self.hm.data(idx, Qt.DisplayRole).value()
|
||||||
tx_hash = tx_item['txid']
|
tx_hash = tx_item['txid']
|
||||||
tx = self.wallet.transactions[tx_hash]
|
tx = self.wallet.transactions[tx_hash]
|
||||||
tx_URL = block_explorer_URL(self.config, 'tx', tx_hash)
|
tx_URL = block_explorer_URL(self.config, 'tx', tx_hash)
|
||||||
@@ -595,4 +598,4 @@ class HistoryList(MyTreeView, AcceptFileDragDrop):
|
|||||||
def text_txid_from_coordinate(self, row, col):
|
def text_txid_from_coordinate(self, row, col):
|
||||||
idx = self.model().mapToSource(self.model().index(row, col))
|
idx = self.model().mapToSource(self.model().index(row, col))
|
||||||
tx_item = self.hm.transactions[idx.row()]
|
tx_item = self.hm.transactions[idx.row()]
|
||||||
return str(self.hm.data(idx, Qt.DisplayRole)), tx_item['txid']
|
return self.hm.data(idx, Qt.DisplayRole).value(), tx_item['txid']
|
||||||
|
|||||||
@@ -398,8 +398,23 @@ def filename_field(parent, config, defaultname, select_msg):
|
|||||||
return vbox, filename_e, b1
|
return vbox, filename_e, b1
|
||||||
|
|
||||||
class ElectrumItemDelegate(QStyledItemDelegate):
|
class ElectrumItemDelegate(QStyledItemDelegate):
|
||||||
def createEditor(self, parent, option, index):
|
def __init__(self, tv):
|
||||||
return self.parent().createEditor(parent, option, index)
|
super().__init__(tv)
|
||||||
|
self.tv = tv
|
||||||
|
self.opened = None
|
||||||
|
def on_closeEditor(editor: QLineEdit, hint):
|
||||||
|
self.opened = None
|
||||||
|
def on_commitData(editor: QLineEdit):
|
||||||
|
new_text = editor.text()
|
||||||
|
idx = QModelIndex(self.opened)
|
||||||
|
_prior_text, user_role = self.tv.text_txid_from_coordinate(idx.row(), idx.column())
|
||||||
|
self.tv.on_edited(idx, user_role, new_text)
|
||||||
|
self.closeEditor.connect(on_closeEditor)
|
||||||
|
self.commitData.connect(on_commitData)
|
||||||
|
|
||||||
|
def createEditor(self, parent, option, idx):
|
||||||
|
self.opened = QPersistentModelIndex(idx)
|
||||||
|
return super().createEditor(parent, option, idx)
|
||||||
|
|
||||||
class MyTreeView(QTreeView):
|
class MyTreeView(QTreeView):
|
||||||
|
|
||||||
@@ -415,8 +430,6 @@ class MyTreeView(QTreeView):
|
|||||||
self.icon_cache = IconCache()
|
self.icon_cache = IconCache()
|
||||||
|
|
||||||
# Control which columns are editable
|
# Control which columns are editable
|
||||||
self.editor = None
|
|
||||||
self.pending_update = False
|
|
||||||
if editable_columns is None:
|
if editable_columns is None:
|
||||||
editable_columns = {stretch_column}
|
editable_columns = {stretch_column}
|
||||||
else:
|
else:
|
||||||
@@ -458,7 +471,9 @@ class MyTreeView(QTreeView):
|
|||||||
self.header().setSectionResizeMode(col, sm)
|
self.header().setSectionResizeMode(col, sm)
|
||||||
|
|
||||||
def keyPressEvent(self, event):
|
def keyPressEvent(self, event):
|
||||||
if event.key() in [ Qt.Key_F2, Qt.Key_Return ] and self.editor is None:
|
if self.itemDelegate().opened:
|
||||||
|
return
|
||||||
|
if event.key() in [ Qt.Key_F2, Qt.Key_Return ]:
|
||||||
self.on_activated(self.selectionModel().currentIndex())
|
self.on_activated(self.selectionModel().currentIndex())
|
||||||
return
|
return
|
||||||
super().keyPressEvent(event)
|
super().keyPressEvent(event)
|
||||||
@@ -469,36 +484,6 @@ class MyTreeView(QTreeView):
|
|||||||
pt.setX(50)
|
pt.setX(50)
|
||||||
self.customContextMenuRequested.emit(pt)
|
self.customContextMenuRequested.emit(pt)
|
||||||
|
|
||||||
def createEditor(self, parent, option, idx):
|
|
||||||
self.editor = QStyledItemDelegate.createEditor(self.itemDelegate(),
|
|
||||||
parent, option, idx)
|
|
||||||
prior_text, user_role = self.text_txid_from_coordinate(idx.row(), idx.column())
|
|
||||||
def editing_finished():
|
|
||||||
print("editing finished")
|
|
||||||
# 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 is None:
|
|
||||||
return
|
|
||||||
if self.editor.text() == prior_text:
|
|
||||||
print("unchanged ignore any 2nd call")
|
|
||||||
self.editor = None # Unchanged - ignore any 2nd call
|
|
||||||
return
|
|
||||||
if not idx.isValid():
|
|
||||||
print("idx not valid")
|
|
||||||
return
|
|
||||||
new_text, _ = self.text_txid_from_coordinate(idx.row(), idx.column())
|
|
||||||
if new_text == prior_text:
|
|
||||||
print("buggy first call", new_text, prior_text)
|
|
||||||
return # Buggy first call on Enter key, item not yet updated
|
|
||||||
self.on_edited(idx, user_role, self.editor.text())
|
|
||||||
self.editor = None
|
|
||||||
self.editor.editingFinished.connect(editing_finished)
|
|
||||||
return self.editor
|
|
||||||
|
|
||||||
def edit(self, idx, trigger=QAbstractItemView.AllEditTriggers, event=None):
|
def edit(self, idx, trigger=QAbstractItemView.AllEditTriggers, event=None):
|
||||||
"""
|
"""
|
||||||
this is to prevent:
|
this is to prevent:
|
||||||
@@ -509,7 +494,7 @@ class MyTreeView(QTreeView):
|
|||||||
|
|
||||||
def on_edited(self, idx: QModelIndex, user_role, text):
|
def on_edited(self, idx: QModelIndex, user_role, text):
|
||||||
self.parent.wallet.set_label(user_role, text)
|
self.parent.wallet.set_label(user_role, text)
|
||||||
self.parent.history_list.update_labels()
|
self.parent.history_model.refresh('on_edited in MyTreeView')
|
||||||
self.parent.update_completions()
|
self.parent.update_completions()
|
||||||
|
|
||||||
def should_hide(self, row):
|
def should_hide(self, row):
|
||||||
@@ -523,7 +508,10 @@ class MyTreeView(QTreeView):
|
|||||||
assert not isinstance(self.model(), QSortFilterProxyModel)
|
assert not isinstance(self.model(), QSortFilterProxyModel)
|
||||||
idx = self.model().index(row_num, column)
|
idx = self.model().index(row_num, column)
|
||||||
item = self.model().itemFromIndex(idx)
|
item = self.model().itemFromIndex(idx)
|
||||||
return item.text(), item.data(Qt.UserRole)
|
user_role = item.data(Qt.UserRole)
|
||||||
|
# check that we didn't forget to set UserRole on an editable field
|
||||||
|
assert user_role is not None, (row_num, column)
|
||||||
|
return item.text(), user_role
|
||||||
|
|
||||||
def hide_row(self, row_num):
|
def hide_row(self, row_num):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ class UTXOList(MyTreeView):
|
|||||||
filter_columns = [0, 1] # Address, Label
|
filter_columns = [0, 1] # Address, Label
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super().__init__(parent, self.create_menu, 1)
|
super().__init__(parent, self.create_menu, 1, editable_columns=[])
|
||||||
self.setModel(QStandardItemModel(self))
|
self.setModel(QStandardItemModel(self))
|
||||||
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
self.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||||
self.setSortingEnabled(True)
|
self.setSortingEnabled(True)
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ class Abstract_Wallet(AddressSynchronizer):
|
|||||||
self.labels[name] = text
|
self.labels[name] = text
|
||||||
changed = True
|
changed = True
|
||||||
else:
|
else:
|
||||||
if old_text:
|
if old_text is not None:
|
||||||
self.labels.pop(name)
|
self.labels.pop(name)
|
||||||
changed = True
|
changed = True
|
||||||
if changed:
|
if changed:
|
||||||
|
|||||||
Reference in New Issue
Block a user