0

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.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
NZD
  • 1,780
  • 2
  • 20
  • 29
  • You can't, and your attempt above is pointless: `initStyleOption` is for *style* options (used for drawing widgets, positioning of controls, etc), QTextOption is a completely different thing. You could use a QTextEdit or QPlainTextEdit with a fixed height based on the font (and frame), and disable the vertical scroll bar. – musicamante Dec 01 '21 at 19:02
  • 1
    See [this question](https://stackoverflow.com/q/56196629/984421) for a C++ solution. Should be easy enough to port it to PyQt/PySide. – ekhumoro Dec 01 '21 at 20:10
  • @ekhumoro Done that, but I can't get the height right. See my answer below. – NZD Dec 05 '21 at 19:36
  • @NZD It looks like that solution doesn't really work, then. I don't really have any better suggestions - although the source code for QLineEdit might give some clues. – ekhumoro Dec 05 '21 at 20:18

1 Answers1

0

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?

NZD
  • 1,780
  • 2
  • 20
  • 29