1

To the point: How do I get a QTreeWidgetItem to respect a QLineEdits setEchoMode(QLineEdit.Password) ?

I've been banging my head against the wall for this for the last day: I have a subclass of QTreeWidgetItem (which simply adds one extra field to the class)

I create an instance of it, add it to my TreeWidget:

    twi = DIMTreeWidgetItem.DIMTreeWidgetItem(uuid.uuid4(), [field_name, '<Empty>'])
    ...
    self.ui_instance.main_window.treeWidget.addTopLevelItem(twi)

I edit that an instance based on a double click of that item with:

self.ui_instance.main_window.treeWidget.editItem(item, column) This works fine.

I have a delegate attached to that column which is simply:

def __init__(self, parent=None, *args):
    QStyledItemDelegate.__init__(self, parent, *args)

def createEditor(self, parent, option, index):        
    le = QLineEdit('', parent)
    le.setEchoMode(QLineEdit.PasswordEchoOnEdit)
    return le

But it seems this only effects the item during editing. What is the correct way for me to obscure the content of the treewidgetitem after editing?

Even if someone can shed some light on how to do this in C++, I'm sure I can translate it to pyside2

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Dnlhoust
  • 118
  • 1
  • 9

1 Answers1

1

You have to overwrite the delegate's displayText method to return the character so many times the length of the text:

from PySide2 import QtCore, QtWidgets

class PasswordDelegate(QtWidgets.QStyledItemDelegate):
    def createEditor(self, parent, option, index): 
        editor = QtWidgets.QLineEdit(parent)
        editor.setEchoMode(QtWidgets.QLineEdit.PasswordEchoOnEdit)
        return editor

    def displayText(self, value, locale):
        character = "●" # u"\u25CF"
        v = character * len(value)
        return super(PasswordDelegate, self).displayText(v, locale)

class EditableItem(QtWidgets.QTreeWidgetItem):
    def __init__(self, *args, **kwargs):
        super(EditableItem, self).__init__(*args, **kwargs)
        self.setFlags(self.flags() | QtCore.Qt.ItemIsEditable)

class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        tree_widget = QtWidgets.QTreeWidget()
        self.setCentralWidget(tree_widget)

        for i in range(5):
            parent_item = EditableItem(tree_widget, ["{}".format(i)])
            for j in range(5):
                child_item = EditableItem(["{}-{}".format(i, j)])
                parent_item.addChild(child_item)
        tree_widget.expandAll()

        delegate = PasswordDelegate(tree_widget)
        tree_widget.setItemDelegate(delegate)

if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.resize(640, 480)
    w.show()
    sys.exit(app.exec_())

If you want to discriminate in which element to apply, you can override the initStyleOption method since it has the QModelIndex information associated with the item, in the following example it will only be applied to the elements that have a parent.

class PasswordDelegate(QtWidgets.QStyledItemDelegate):
    def createEditor(self, parent, option, index): 
        editor = QtWidgets.QLineEdit(parent)
        editor.setEchoMode(QtWidgets.QLineEdit.PasswordEchoOnEdit)
        return editor

    def initStyleOption(self, option, index):
        super(PasswordDelegate, self).initStyleOption(option, index)
        if index.parent().isValid():
            character = "●" # u"\u25CF"
            option.text = character * len(option.text)

enter image description here

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
  • Thanks; Can you advise on how this can be applied selectively? displayText doesn't contain enough information to determine the widget item calling it - I'd like to be able to query parts of the widget item so I can decide what should be obscured. Perhaps overriding what calls displayText as well, so that I can pass through more parameters? – Dnlhoust Dec 30 '18 at 15:57
  • @Dnlhoust The criterion could be indicated, the delegate only has information of the model, not of the view. – eyllanesc Dec 30 '18 at 15:59
  • 1
    @Dnlhoust If you want to use the information of the view you must do it through the model, that is, you must use a role: `your_QTreeWidgetItem.setData(some_col, QtCore.Qt.UserRole + 1000, some_value)` and then you can get value in initStyleOption with `some_value = index. data(QtCore.Qt.UserRole + 1000)` – eyllanesc Dec 30 '18 at 16:10