Search code examples
pythonpyside2qlineedit

Python: How to set option "ShowTabsAndSpaces" in QLineEdit


I have a QLineEdit widget to enter text (a simple search pattern) that can include spaces.

You can't see the space characters, this is especially obvious for trailing spaces. I know you can set option ShowTabsAndSpaces by calling method setDefaultTextOption on the document of a QTextEdit widget, but is there a way to set this option (or something similar) on a QLineEdit widget?

Method setDefaultTextOption is not available for QLineEdit widgets, so I tried:

item = QLineEdit(value)
option = QTextOption()
option.setFlags(QTextOption.ShowTabsAndSpaces)
item.initStyleOption(option)

But that gives me an exception:

<class 'TypeError'>: 
'PySide2.QtWidgets.QLineEdit.initStyleOption' called with wrong argument types:
  PySide2.QtWidgets.QLineEdit.initStyleOption(PySide2.QtGui.QTextOption)
Supported signatures:
  PySide2.QtWidgets.QLineEdit.initStyleOption(PySide2.QtWidgets.QStyleOptionFrame)

How do I set option ShowTabsAndSpaces on a QLineEdit widget or is there another way to make space characters visible in a QLineEdit widget. I would be happy with only space characters that are visible.


Solution

  • Used the suggestion of @ekhumoro to port https://stackoverflow.com/a/56298439/4459346

    This resulted in:

    #!/usr/bin/env python3
    
    import sys
    import time
    from PySide2.QtWidgets import (QLineEdit, QPushButton, QApplication,
        QVBoxLayout, QDialog, QMessageBox, QPlainTextEdit, QGridLayout)
    from PySide2.QtCore import (Qt, QMimeData)
    from PySide2.QtGui import (QTextOption, QFontMetrics)
    
    # source: https://stackoverflow.com/a/56298439/4459346
    class SpacesLineEdit(QPlainTextEdit):
        def __init__(self, text=None):
            if text:
                super().__init__(text)
            else:
                super().__init__()
            self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
            self.setLineWrapMode(QPlainTextEdit.NoWrap)
            self.setTabChangesFocus(True)
    
            option = QTextOption()
            option.setFlags(QTextOption.ShowTabsAndSpaces)
            self.document().setDefaultTextOption(option)
    
            # limit height to one line
            self.setHeight(1)
    
            # Stealing the sizeHint from a plain QLineEdit will do for now :-P
            self._sizeHint = QLineEdit().sizeHint()
    
        def minimumSizeHint(self):
            return self._sizeHint
    
        def sizeHint(self):
            return self._sizeHint
    
        def keyPressEvent(self, event):
            if event.key() == Qt.Key_Return or event.key() == Qt.Key_Enter:
                event.ignore()
                return
            super().keyPressEvent(event)
    
        def insertFromMimeData(self, source):
            text = source.text()
            text = text.replace(str('\r\n'), str(' '))
            text = text.replace(str('\n'), str(' '))
            text = text.replace(str('\r'), str(' '))
            processedSource = QMimeData()
            processedSource.setText(text)
            super().insertFromMimeData(processedSource)
    
        def setText(self, text):
            self.setPlainText(text)
    
        def text(self):
            return self.toPlainText()
    
        def setHeight1(self):
            # see 
            doc = self.document()
            b = doc.begin()
            layout = doc.documentLayout()
            h = layout.blockBoundingRect(b).height()
            self.setFixedHeight(h  + 2 * self.frameWidth() + 1)
    
        def setHeight(self, nRows):
            # source:  https://stackoverflow.com/a/46997337/4459346
            pdoc = self.document()
            fm = QFontMetrics(pdoc.defaultFont())
            lineSpacing = fm.lineSpacing()
            print(lineSpacing)  # todo:test
            margins = self.contentsMargins()
            nHeight = lineSpacing * nRows + \
                (pdoc.documentMargin() + self.frameWidth()) * 2 + \
                margins.top() + margins.bottom()
            self.setFixedHeight(nHeight)
    
    
    class Form(QDialog):
    
        def __init__(self, parent=None):
            super(Form, self).__init__(parent)
            demoText = " some text with spaces "
            self.lineedit0 = QLineEdit(demoText)
            self.lineedit1 = SpacesLineEdit(demoText)
            self.lineedit2 = QLineEdit(demoText)
            self.lineedit3 = QLineEdit(demoText)
            layout = QGridLayout()
            layout.addWidget(self.lineedit0, 0, 0)
            layout.addWidget(self.lineedit1, 1, 0)
            layout.addWidget(self.lineedit2, 0, 1)
            layout.addWidget(self.lineedit3, 1, 1)
            self.setLayout(layout)
        
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        form = Form()
        print('starting app...')
        form.show()
        sys.exit(app.exec_())
    

    The spaces are now shown, just as I wanted.

    The only thing I had to add was a method to set the height. See method setHeight, which I copied from https://stackoverflow.com/a/46997337/4459346.

    The original method tries to calculate the exact line height, but seems it doesn't get the height exactly right:

    qlineEdit vs QPlainTextEdit

    The one on the bottom-left is the QPlainTextEdit, the other three are just qlineEdit widgets.

    I can fix the incorrect height by subtracting a few pixels, but I rather not do that without knowing what I'm doing.

    Anyone, any suggestions to improve the code in method setHeight?