Eddie
Eddie

Reputation: 54421

How do I make a PyQt QLineEdit widget narrower than the default size of 17 x's when using QT Designer?

When I put Qt widgets in a QHBoxLayout layout using QT Designer, there seems to be a minimum width for QLineEdit widgets of 17 x's, at least if this is the current source code:

https://github.com/qt/qtbase/blob/dev/src/widgets/widgets/qlineedit.cpp

I cannot find a way to make PyQt5 lay those widgets out so they will be narrower than this default, but still change size if the font is changed.

As an example, a QComboBox will be automatically laid out so that it is just wide enough to display the longest text that is entered as a possible value. If the longest text entered for a combobox in Designer is 5 characters, the combobox will be laid out as quite narrow compared to the minimum QLineEdit width. How can I configure a QLineEdit with a number of characters so that it is always wide enough for that many characters, whatever the font is set to, and no wider, using just QT Designer? I know in Designer I can enter maxLength, which will limit the maximum number of characters that can be entered/displayed, but that setting of course has no effect on the layout.

I have some text boxes that will never have more than 5 characters in them, for example, and the layout with Designer makes them at least 3 times wider than I will ever need. This is using the default "Expanding" horizontal policy, but I have tried many combinations of horizontal policy and values for minimum size or base size. I want to allow for people to have different font sizes, so I cannot safely set a maximum pixel size and I cannot safely set a fixed horizontal size. The handling for QComboBoxes is precisely what I want for QLineEdits.

This is all in python using the latest versions of PyQt5 available on Pip, pyqt5 5.15.6 and pyqt5-qt5 5.15.2.

Upvotes: 1

Views: 1564

Answers (1)

musicamante
musicamante

Reputation: 48231

The size hint of QLineEdit is computed considering various aspects, most of them using private functions that are not exposed to the API, and the "x" count is hardcoded, meaning that this cannot be achieved directly from Designer, and can only be done through subclassing.

While we could try to mimic its behavior to implement a custom character size, I believe it is unnecessary for simple cases, so I simplified the concept by taking the default size hint and adapting the width based on the difference between the custom character hint and the default 17 "x" count.

class CharHintLineEdit(QtWidgets.QLineEdit):
    _charHint = 17
    @QtCore.pyqtProperty(int)
    def charHint(self):
        return self._charHint

    @charHint.setter
    def charHint(self, chars):
        chars = max(1, chars)
        if self._charHint != chars:
            self._charHint = chars
            self.updateGeometry()

    def changeEvent(self, event):
        super().changeEvent(event)
        if event.type() in (event.FontChange, event.StyleChange):
            self.updateGeometry()

    def sizeHint(self):
        hint = super().sizeHint()
        if self._charHint != 17:
            # the 17 char width is hardcoded in Qt and there is no way to
            # retrieve it, it might change in the future, so, just to be safe,
            # we always set an arbitrary minimum based on half the height hint
            charSize = self.fontMetrics().horizontalAdvance('x')
            hint.setWidth(max(hint.height() // 2, hint.width() + 
                charSize * (self._charHint - 17)))
        return hint


if __name__ == '__main__':
    import sys
    from random import randrange
    app = QtWidgets.QApplication(sys.argv)
    test = QtWidgets.QWidget()
    layout = QtWidgets.QHBoxLayout(test)
    for i in range(5):
        charHint = randrange(5, 15)
        le = CharHintLineEdit(charHint=charHint, placeholderText=str(charHint))
        layout.addWidget(le)
    test.show()
    sys.exit(app.exec())

With the above code, you can use the custom widget in Designer by adding a standard QLineEdit and promoting it with the class name and relative python file (without the file extension) as header. You can also set the charHint as a dynamic property in Designer, and it will be properly set for the widget when the UI is loaded.

Upvotes: 3

Related Questions