Rework MyTreeWidget editing
Gets rid of need for EditableItem class. New callback on_permit_edit to permit widgets to refuse editing. Restores popup menu on activating a non-editable column behaviour.
This commit is contained in:
parent
c481e61417
commit
544b829f6e
|
@ -32,7 +32,6 @@ class HistoryWidget(MyTreeWidget):
|
||||||
self.refresh_headers()
|
self.refresh_headers()
|
||||||
self.setColumnHidden(1, True)
|
self.setColumnHidden(1, True)
|
||||||
self.config = self.parent.config
|
self.config = self.parent.config
|
||||||
self.setSortingEnabled(False)
|
|
||||||
|
|
||||||
def refresh_headers(self):
|
def refresh_headers(self):
|
||||||
headers = ['', '', _('Date'), _('Description') , _('Amount'),
|
headers = ['', '', _('Date'), _('Description') , _('Amount'),
|
||||||
|
@ -72,7 +71,7 @@ class HistoryWidget(MyTreeWidget):
|
||||||
label, is_default_label = self.wallet.get_label(tx_hash)
|
label, is_default_label = self.wallet.get_label(tx_hash)
|
||||||
entry = ['', tx_hash, time_str, label, v_str, balance_str]
|
entry = ['', tx_hash, time_str, label, v_str, balance_str]
|
||||||
run_hook('history_tab_update', tx, entry)
|
run_hook('history_tab_update', tx, entry)
|
||||||
item = EditableItem(entry)
|
item = QTreeWidgetItem(entry)
|
||||||
item.setIcon(0, icon)
|
item.setIcon(0, icon)
|
||||||
for i in range(len(entry)):
|
for i in range(len(entry)):
|
||||||
if i>3:
|
if i>3:
|
||||||
|
|
|
@ -855,7 +855,7 @@ class ElectrumWindow(QMainWindow, PrintError):
|
||||||
requestor = req.get('name', '')
|
requestor = req.get('name', '')
|
||||||
amount_str = self.format_amount(amount) if amount else ""
|
amount_str = self.format_amount(amount) if amount else ""
|
||||||
account = ''
|
account = ''
|
||||||
item = EditableItem([date, account, address, '', message, amount_str, pr_tooltips.get(status,'')])
|
item = QTreeWidgetItem([date, account, address, '', message, amount_str, pr_tooltips.get(status,'')])
|
||||||
if signature is not None:
|
if signature is not None:
|
||||||
item.setIcon(3, QIcon(":icons/seal.png"))
|
item.setIcon(3, QIcon(":icons/seal.png"))
|
||||||
item.setToolTip(3, 'signed by '+ requestor)
|
item.setToolTip(3, 'signed by '+ requestor)
|
||||||
|
@ -921,7 +921,6 @@ class ElectrumWindow(QMainWindow, PrintError):
|
||||||
self.from_label = QLabel(_('From'))
|
self.from_label = QLabel(_('From'))
|
||||||
grid.addWidget(self.from_label, 3, 0)
|
grid.addWidget(self.from_label, 3, 0)
|
||||||
self.from_list = MyTreeWidget(self, self.from_list_menu, ['',''])
|
self.from_list = MyTreeWidget(self, self.from_list_menu, ['',''])
|
||||||
self.from_list.setSortingEnabled(False)
|
|
||||||
self.from_list.setHeaderHidden(True)
|
self.from_list.setHeaderHidden(True)
|
||||||
self.from_list.setMaximumHeight(80)
|
self.from_list.setMaximumHeight(80)
|
||||||
grid.addWidget(self.from_list, 3, 1, 1, 3)
|
grid.addWidget(self.from_list, 3, 1, 1, 3)
|
||||||
|
@ -1000,6 +999,7 @@ class ElectrumWindow(QMainWindow, PrintError):
|
||||||
self.invoices_label = QLabel(_('Invoices'))
|
self.invoices_label = QLabel(_('Invoices'))
|
||||||
self.invoices_list = MyTreeWidget(self, self.invoices_list_menu,
|
self.invoices_list = MyTreeWidget(self, self.invoices_list_menu,
|
||||||
[_('Expires'), _('Requestor'), _('Description'), _('Amount'), _('Status')], 2)
|
[_('Expires'), _('Requestor'), _('Description'), _('Amount'), _('Status')], 2)
|
||||||
|
self.invoices_list.setSortingEnabled(True)
|
||||||
self.invoices_list.header().setResizeMode(1, QHeaderView.Interactive)
|
self.invoices_list.header().setResizeMode(1, QHeaderView.Interactive)
|
||||||
self.invoices_list.setColumnWidth(1, 200)
|
self.invoices_list.setColumnWidth(1, 200)
|
||||||
|
|
||||||
|
@ -1406,14 +1406,15 @@ class ElectrumWindow(QMainWindow, PrintError):
|
||||||
def create_addresses_tab(self):
|
def create_addresses_tab(self):
|
||||||
l = MyTreeWidget(self, self.create_receive_menu, [ _('Address'), _('Label'), _('Balance'), _('Tx')], 1)
|
l = MyTreeWidget(self, self.create_receive_menu, [ _('Address'), _('Label'), _('Balance'), _('Tx')], 1)
|
||||||
l.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
l.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||||
l.setSortingEnabled(False)
|
|
||||||
self.address_list = l
|
self.address_list = l
|
||||||
return self.create_list_tab(l)
|
return self.create_list_tab(l)
|
||||||
|
|
||||||
def create_contacts_tab(self):
|
def create_contacts_tab(self):
|
||||||
l = MyTreeWidget(self, self.create_contact_menu, [_('Name'), _('Value'), _('Type')], 1, [0, 1])
|
l = MyTreeWidget(self, self.create_contact_menu, [_('Name'), _('Value'), _('Type')], 1, [0, 1])
|
||||||
l.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
l.setSelectionMode(QAbstractItemView.ExtendedSelection)
|
||||||
l.item_edited = self.contact_edited
|
l.setSortingEnabled(True)
|
||||||
|
l.on_edited = self.on_contact_edited
|
||||||
|
l.on_permit_edit = self.on_permit_contact_edit
|
||||||
self.contacts_list = l
|
self.contacts_list = l
|
||||||
return self.create_list_tab(l)
|
return self.create_list_tab(l)
|
||||||
|
|
||||||
|
@ -1427,7 +1428,7 @@ class ElectrumWindow(QMainWindow, PrintError):
|
||||||
requestor = pr.get_requestor()
|
requestor = pr.get_requestor()
|
||||||
exp = pr.get_expiration_date()
|
exp = pr.get_expiration_date()
|
||||||
date_str = util.format_time(exp) if exp else _('Never')
|
date_str = util.format_time(exp) if exp else _('Never')
|
||||||
item = EditableItem([date_str, requestor, pr.memo, self.format_amount(pr.get_amount(), whitespaces=True), pr_tooltips.get(status,'')])
|
item = QTreeWidgetItem([date_str, requestor, pr.memo, self.format_amount(pr.get_amount(), whitespaces=True), pr_tooltips.get(status,'')])
|
||||||
item.setIcon(4, QIcon(pr_icons.get(status)))
|
item.setIcon(4, QIcon(pr_icons.get(status)))
|
||||||
item.setData(0, Qt.UserRole, key)
|
item.setData(0, Qt.UserRole, key)
|
||||||
item.setFont(1, QFont(MONOSPACE_FONT))
|
item.setFont(1, QFont(MONOSPACE_FONT))
|
||||||
|
@ -1551,7 +1552,11 @@ class ElectrumWindow(QMainWindow, PrintError):
|
||||||
self.payto_e.setText(text)
|
self.payto_e.setText(text)
|
||||||
self.payto_e.setFocus()
|
self.payto_e.setFocus()
|
||||||
|
|
||||||
def contact_edited(self, item, column, prior):
|
def on_permit_contact_edit(self, item, column):
|
||||||
|
# openalias items shouldn't be editable
|
||||||
|
return item.text(2) != "openalias"
|
||||||
|
|
||||||
|
def on_contact_edited(self, item, column, prior):
|
||||||
if column == 0: # Remove old contact if renamed
|
if column == 0: # Remove old contact if renamed
|
||||||
self.contacts.pop(prior)
|
self.contacts.pop(prior)
|
||||||
self.set_contact(unicode(item.text(0)), unicode(item.text(1)))
|
self.set_contact(unicode(item.text(0)), unicode(item.text(1)))
|
||||||
|
@ -1703,7 +1708,7 @@ class ElectrumWindow(QMainWindow, PrintError):
|
||||||
label = self.wallet.labels.get(address,'')
|
label = self.wallet.labels.get(address,'')
|
||||||
c, u, x = self.wallet.get_addr_balance(address)
|
c, u, x = self.wallet.get_addr_balance(address)
|
||||||
balance = self.format_amount(c + u + x)
|
balance = self.format_amount(c + u + x)
|
||||||
item = EditableItem( [ address, label, balance, "%d"%num] )
|
item = QTreeWidgetItem([address, label, balance, "%d"%num])
|
||||||
item.setFont(0, QFont(MONOSPACE_FONT))
|
item.setFont(0, QFont(MONOSPACE_FONT))
|
||||||
item.setData(0, Qt.UserRole, address)
|
item.setData(0, Qt.UserRole, address)
|
||||||
item.setData(0, Qt.UserRole+1, True) # label can be edited
|
item.setData(0, Qt.UserRole+1, True) # label can be edited
|
||||||
|
@ -1729,10 +1734,7 @@ class ElectrumWindow(QMainWindow, PrintError):
|
||||||
l.clear()
|
l.clear()
|
||||||
for key in sorted(self.contacts.keys()):
|
for key in sorted(self.contacts.keys()):
|
||||||
_type, value = self.contacts[key]
|
_type, value = self.contacts[key]
|
||||||
if _type == 'address':
|
item = QTreeWidgetItem([key, value, _type])
|
||||||
item = EditableItem([key, value, _type])
|
|
||||||
else: # openalias items shouldn't be editable
|
|
||||||
item = QTreeWidgetItem([key, value, _type])
|
|
||||||
item.setData(0, Qt.UserRole, key)
|
item.setData(0, Qt.UserRole, key)
|
||||||
l.addTopLevelItem(item)
|
l.addTopLevelItem(item)
|
||||||
if key == current_key:
|
if key == current_key:
|
||||||
|
|
|
@ -283,19 +283,9 @@ def filename_field(parent, config, defaultname, select_msg):
|
||||||
|
|
||||||
return vbox, filename_e, b1
|
return vbox, filename_e, b1
|
||||||
|
|
||||||
class EditableItem(QTreeWidgetItem):
|
class ElectrumItemDelegate(QStyledItemDelegate):
|
||||||
def __init__(self, columns):
|
|
||||||
QTreeWidgetItem.__init__(self, columns)
|
|
||||||
self.setFlags(self.flags() | Qt.ItemIsEditable)
|
|
||||||
|
|
||||||
class EditableItemDelegate(QStyledItemDelegate):
|
|
||||||
def createEditor(self, parent, option, index):
|
def createEditor(self, parent, option, index):
|
||||||
if index.column() not in self.parent().editable_columns:
|
return self.parent().createEditor(parent, option, index)
|
||||||
return None
|
|
||||||
self.parent().editing = (self.parent().currentItem(),
|
|
||||||
index.column(),
|
|
||||||
unicode(index.data().toString()))
|
|
||||||
return QStyledItemDelegate.createEditor(self, parent, option, index)
|
|
||||||
|
|
||||||
class MyTreeWidget(QTreeWidget):
|
class MyTreeWidget(QTreeWidget):
|
||||||
|
|
||||||
|
@ -305,23 +295,20 @@ class MyTreeWidget(QTreeWidget):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.stretch_column = stretch_column
|
self.stretch_column = stretch_column
|
||||||
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
self.setContextMenuPolicy(Qt.CustomContextMenu)
|
||||||
self.itemActivated.connect(self.on_activated)
|
|
||||||
self.customContextMenuRequested.connect(create_menu)
|
self.customContextMenuRequested.connect(create_menu)
|
||||||
|
self.setUniformRowHeights(True)
|
||||||
# extend the syntax for consistency
|
# extend the syntax for consistency
|
||||||
self.addChild = self.addTopLevelItem
|
self.addChild = self.addTopLevelItem
|
||||||
self.insertChild = self.insertTopLevelItem
|
self.insertChild = self.insertTopLevelItem
|
||||||
|
|
||||||
# Control which columns are editable
|
# Control which columns are editable
|
||||||
self.editing = (None, None, None)
|
self.editor = None
|
||||||
if editable_columns is None:
|
if editable_columns is None:
|
||||||
editable_columns = [stretch_column]
|
editable_columns = [stretch_column]
|
||||||
self.editable_columns = editable_columns
|
self.editable_columns = editable_columns
|
||||||
self.setEditTriggers(QAbstractItemView.DoubleClicked |
|
self.setItemDelegate(ElectrumItemDelegate(self))
|
||||||
QAbstractItemView.EditKeyPressed)
|
self.itemActivated.connect(self.on_activated)
|
||||||
self.setItemDelegate(EditableItemDelegate(self))
|
|
||||||
self.itemChanged.connect(self.item_changed)
|
|
||||||
self.update_headers(headers)
|
self.update_headers(headers)
|
||||||
self.setSortingEnabled(True)
|
|
||||||
|
|
||||||
def update_headers(self, headers):
|
def update_headers(self, headers):
|
||||||
self.setColumnCount(len(headers))
|
self.setColumnCount(len(headers))
|
||||||
|
@ -331,26 +318,61 @@ class MyTreeWidget(QTreeWidget):
|
||||||
sm = QHeaderView.Stretch if col == self.stretch_column else QHeaderView.ResizeToContents
|
sm = QHeaderView.Stretch if col == self.stretch_column else QHeaderView.ResizeToContents
|
||||||
self.header().setResizeMode(col, sm)
|
self.header().setResizeMode(col, sm)
|
||||||
|
|
||||||
def on_activated(self, item):
|
def editItem(self, item, column):
|
||||||
if not item:
|
if column in self.editable_columns:
|
||||||
return
|
self.editing_itemcol = (item, column, unicode(item.text(column)))
|
||||||
for i in range(0,self.viewport().height()/5):
|
# Calling setFlags causes on_changed events for some reason
|
||||||
if self.itemAt(QPoint(0,i*5)) == item:
|
item.setFlags(item.flags() | Qt.ItemIsEditable)
|
||||||
break
|
QTreeWidget.editItem(self, item, column)
|
||||||
|
item.setFlags(item.flags() & ~Qt.ItemIsEditable)
|
||||||
|
|
||||||
|
def keyPressEvent(self, event):
|
||||||
|
if event.key() == Qt.Key_F2:
|
||||||
|
self.on_activated(self.currentItem(), self.currentColumn())
|
||||||
else:
|
else:
|
||||||
return
|
QTreeWidget.keyPressEvent(self, event)
|
||||||
for j in range(0,30):
|
|
||||||
if self.itemAt(QPoint(0,i*5 + j)) != item:
|
|
||||||
break
|
|
||||||
self.emit(SIGNAL('customContextMenuRequested(const QPoint&)'), QPoint(50, i*5 + j - 1))
|
|
||||||
|
|
||||||
def item_changed(self, item, column):
|
def permit_edit(self, item, column):
|
||||||
'''Called only when the text actually changes'''
|
return (column in self.editable_columns
|
||||||
# Only pass user edits to item_edited()
|
and self.on_permit_edit(item, column))
|
||||||
if item == self.editing[0] and column == self.editing[1]:
|
|
||||||
self.item_edited(item, column, self.editing[2])
|
|
||||||
|
|
||||||
def item_edited(self, item, column, prior):
|
def on_permit_edit(self, item, column):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def on_activated(self, item, column):
|
||||||
|
if self.permit_edit(item, column):
|
||||||
|
self.editItem(item, column)
|
||||||
|
else:
|
||||||
|
pt = self.visualItemRect(item).bottomLeft()
|
||||||
|
pt.setX(50)
|
||||||
|
self.emit(SIGNAL('customContextMenuRequested(const QPoint&)'), pt)
|
||||||
|
|
||||||
|
def createEditor(self, parent, option, index):
|
||||||
|
self.editor = QStyledItemDelegate.createEditor(self.itemDelegate(),
|
||||||
|
parent, option, index)
|
||||||
|
self.editor.connect(self.editor, SIGNAL("editingFinished()"),
|
||||||
|
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
|
||||||
|
|
||||||
|
def on_edited(self, item, column, prior):
|
||||||
'''Called only when the text actually changes'''
|
'''Called only when the text actually changes'''
|
||||||
key = str(item.data(0, Qt.UserRole).toString())
|
key = str(item.data(0, Qt.UserRole).toString())
|
||||||
text = unicode(item.text(column))
|
text = unicode(item.text(column))
|
||||||
|
|
Loading…
Reference in New Issue