I would like to change the text display of some QLineEdit
's.
The text should be displayed as a euro amount.
QLineEdits do not seem to be the appropriate choice for such a display. I therefore adapt the code to QDoubleSpinBox. With self.setButtonSymbols(QDoubleSpinBox.NoButtons)
the graphical representation of the QDoubleSpinBoxes is identical to that of QLineEdits
Briefly so that the context is clear:
I have several QTableWidgets in which I use custom delegates such as:
class EuroDelegate(QStyledItemDelegate):
def displayText(self, value, locale):
if value is not None:
value = float(value)
return locale.toCurrencyString(value, '€', 2)
else:
return super().displayText(value, locale)
to display the text as a euro amount.
Some QLineEdits also contain euro amounts and I want them to "feel" as similar as possible. Therefore I changed those QLineEdits to QDoubleSpinBoxes.
As a result, some of the desired properties are quickly achieved.
striked items on the list are already implemented in the code snippet below:
LineEdit.text()
.Edit:
Further explanation to 5.): Since I may have expressed myself unclear:
I would like that inputs that would fall above or below the setrange are not blocked, but that instead a comma is added if possible (check if there is already a comma?) and the input is continued instantly in the decimal point area.
A code snippet to play around with:
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QDoubleSpinBox, QLineEdit, QPushButton
from PyQt5.QtCore import QTimer
class EuroDoubleSpinBox(QDoubleSpinBox):
def __init__(self, parent=None):
super().__init__(parent)
self.setButtonSymbols(QDoubleSpinBox.NoButtons)
self.normalSuffix = ''
self.currencySuffix = ' €'
self.setSuffix(self.currencySuffix)
def focusInEvent(self, event):
self.setSuffix(self.normalSuffix)
QTimer.singleShot(0, self.selectAll)
super().focusInEvent(event)
def focusOutEvent(self, event):
self.setSuffix(self.currencySuffix)
super().focusOutEvent(event)
class MainWindow(QWidget):
def __init__(self):
super().__init__()
self.initUI()
def initUI(self):
layout = QVBoxLayout()
self.pushbutton = QPushButton('Print DoubleSpinBox Value')
self.lineedit = QLineEdit() # added for focus toggling
self.doubleSpinBox = EuroDoubleSpinBox()
self.doubleSpinBox.setRange(-99.99, 99.99)
self.doubleSpinBox.setSingleStep(0.01)
layout.addWidget(self.lineedit)
layout.addWidget(self.doubleSpinBox)
layout.addWidget(self.pushbutton)
self.setLayout(layout)
self.pushbutton.clicked.connect(lambda: print(self.doubleSpinBox.cleanText()))
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.setGeometry(100, 100, 200, 100)
window.setWindowTitle('Euro DoubleSpinBox')
window.show()
sys.exit(app.exec_())
I'm using python 3.10.13 and PyQt5 5.15.9
QAbstractSpinBox subclasses provide a validate()
function that calls an internal validator, similarly to what validate()
of QValidator does.
The solution is to override that function according to your needs.
For such a simple requirement (add the decimal point when the third digit is being typed), the following should probably suffice:
class EuroDoubleSpinBox(QDoubleSpinBox):
...
def validate(self, text, pos):
if 2 < len(text.lstrip('-')) < 5:
if text.isdecimal():
p = 2
elif text.startswith('-') and text[1:].isdecimal() and len(text) > 3:
p = 3
else:
return super().validate(text, pos)
value = float('{}.{}'.format(text[:p], text[p:]))
if self.minimum() <= value <= self.maximum():
return (
QValidator.Acceptable,
text[:p] + QLocale().decimalPoint() + text[p:],
pos + 1
)
return super().validate(text, pos)
The procedure is the following:
0-9
range, similar to \d?
for regex), assume that the decimal point goes after the second digit;QValidator.Acceptable
, the text with the decimal point defined by the locale, and augment the cursor position by one;The above should work fine for almost any case, including pasting from keyboard. More fine tuning might be required if you also want to support pasting float values using the dot as decimal point while the system uses a different symbol (like in your case).