How to get comma as decimal separator in line edits?
-
Hi
I am a beginner at this: Creating an application in Python using QT5.My application maintains and displays data stored in an Sqlite database.
I use a QSqlRelationalTableModel, and I map data to comboboxes
and lineedits using a QDataWidgetMapper. I also use QDoubleValidator to validate
user input of numbers with decimals. The model uses OnFieldChange as edit-strategy.It is necessary that comma is used as decimal seprator in the user
interface. The OS is configured to use comma as decimal separator, and
QT Designer shows "Norwegian Bokmal, Norway" as locale in the QMainWindow locale
property.The problem: Decimal number are still shown with point as decimal separator in all the
lineedits. I have spent considerable time reading documentation, but I am still
bewildered. I will be gratefull for an help on this. An explanation on how to
go about to have my decimal numbers displayed in the line edits
with comma as decimal seprator.By the way: The QDoubleValidator accepts comma and not dot, so that works
as I want it to.Oh, and I use PyQt5.
-
-
I
@JonB said in How to get comma as decimal separator in line edits?:
...That's something at design-time. Start by verifying what the locale is at runtime.
Hi,
I run this:
q=QtCore.QLocale()
print ('country',q.country())
print ('decimalpoint ', q.decimalPoint())
print ('language ', q.language())Output is:
country 161
decimalpoint ,
language 85country 161 is QLocale::Norway
language 85 is QLocale::NorwegianBokmalAnd that is what I expected.
-
Hi and welcome to devnet,
Do you have the same issue if you create a QLineEdit with code that is not mapped to your model ?
-
@SGaist
So I tried this:number = 8.237 self.test2LineEdit.setText( QtCore.QLocale().toString(number,\ 'f', 2))
test2LineEdit shows 8,24
Decimal comma is used, and the number has been correctly rounded up.
I thought the mapper would have done this, but obviously it did not. -
@Denni-0
My project is a private learning project. I have no dead-line, and I am free to reconsider my approach. Guess I have to do som reading on MCV. Are the models provided by Qt really so troublesome as your post gives me the impression of ? Sent you a PM (or was it a chat) -
I find Denni 0's post very interesting, but I would definitely appreciate a follow up on my original question. I would like to know if I can finish the application with my approach.
I use QSqlRelationalTableModel and QDataWidgetMapper. How can I display numeric data from the database in lineedits according to my locale, with decimal comma instead of decimal point?
Please see posts above.
-
@bkvldr
There are plenty of people and code using both the Qt Designer and theQSql...
classes very happily. @Denni-0 has his own opinions about this, which is fair enough, but (respectfully) they are not the mainstream/majority views. Make of that as you choose.I use QSqlRelationalTableModel and QDataWidgetMapper. How can I display numeric data from the database in lineedits according to my locale, with decimal comma instead of decimal point?
Since you show it does not seem to do this left to its own devices (admittedly surprising, possibly worth a bug report), but your test code with a
QLineEdit
does what you want, have you seen https://doc.qt.io/qt-5/qdatawidgetmapper.html#setItemDelegate which would allow you to use your code in a delegate? I'm not sure how you would, perhaps, achieve your own widget only for numbers in a line edit while forwarding all others to the original delegate, but you could investigate. -
@bkvldr said in How to get comma as decimal separator in line edits?:
I would like to know if I can finish the application with my approach.
I use QSqlRelationalTableModel and QDataWidgetMapper. How can I display numeric data from the database in lineedits according to my locale, with decimal comma instead of decimal point?
Understanding the root cause
The behaviour that you see is caused by something a lot lower-level than
QSqlRelationalTableModel
andQDataWidgetMapper
. It boils down to this (written in C++, but hopefully easy enough to understand for a Python developer):QLocale::setDefault(QLocale::Norwegian); QLineEdit le; le.setText( le.locale().toString(12.34) ); // Shows "12,34" le.setText( QString::number(12.34) ); // Shows "12.34" le.setProperty("text", 12.34); // Shows "12.34"
QDataWidgetMapper
does not know about the existence ofQLineEdit
. It relies onQAbstractItemDelegate
to transfer data to/from widgets, andQAbstractItemDelegate
in turn relies on the Qt property system to do data conversions.Now,
QLineEdit
's USER property (see https://doc.qt.io/qt-5/properties.html ) is "text", which is aQString
instead of adouble
. Some kind of conversion is required.Unless the caller explicitly uses a locale-aware conversion, number-to-string conversions assume an en-US locale. See https://doc.qt.io/qt-5/qstring.html#number, for example: "The formatting always uses QLocale::C, i.e., English/UnitedStates. To get a localized string representation of a number, use QLocale::toString() with the appropriate locale."
Solution(s)
In a nutshell, you need to somehow invoke a locale-aware number-to-string conversion.
As @JonB mentioned, you could implement your own
QAbstractItemDelegate
to do this conversion before passing a stringified number to yourQLineEdit
.Another way is to subclass
QLineEdit
and specialize it for numeric data:class NumericLineEdit : public QLineEdit { Q_OBJECT Q_PROPERTY(double number READ number WRITE setNumber USER true) public: // ... double number() const; void setNumber(double num); // In here, do a locale-aware conversion before calling QLineEdit::setText() };
Yet another way is to avoid using
QLineEdit
and useQDoubleSpinBox
instead. -
@JKSH said in How to get comma as decimal separator in line edits?:
As @JonB mentioned, you could implement your own QAbstractItemDelegate to do this conversion before passing a stringified number to your QLineEdit.
Now you have confused me about my own earlier answer! :)
I thought there was only
QDataWidgetMapper::setItemDelegate()
to affect what widget it used for everything. Now I see there isQDataWidgetMapper::addMapping()
for different widget per column. Given which, subclassQLineEdit
is easiest. So I don't get what the point of thesetItemDelegate()
is over theQDataWidgetMapper
as a whole is when you can set the widget per column? -
@JonB said in How to get comma as decimal separator in line edits?:
So I don't get what the point of the
setItemDelegate()
is over theQDataWidgetMapper
as a whole is when you can set the widget per column?I listed 3 different ways to tackle the problem, from hardest to easiest. @bkvldr mentioned that this is a learning project, so the point here is to provide an opportunity to explore different parts of Qt. The point is not to showcase the Best Solution™.
Given which, subclass
QLineEdit
is easiest.Actually, I'd say "use
QDoubleSpinBox
" is easiest.@Denni-0 said in How to get comma as decimal separator in line edits?:
while the end issue might be exactly what you describe it is not the core issue. The core issue is implementing a solution as a direct from the Database into GUI process as the MVC methodology version does not have this issue because the data when presented to the GUI is in format that it can easily consume and the data when presented to the Database is in a format that it can easily consume.
Even with the MVC methodology, we still need to be aware that certain data conversion pathways do not take the locale into account, and we must program our MVC components accordingly.
This issue affects more than just choosing an architecture for transferring data between front-ends and back-ends. It also affects things like exporting data to CSV files; it also affects non-GUI applications.
The key is... how can we simplify it and yet make it as smart as possible or going to the model of K.I.S.S. (Keep It Simple and Smart). I have seen programmers create nightmares by trying to get fancy and slick when something a lot simpler would have been a whole lot smarter.
I agree with you that keeping things simple and smart is definitely desirable. What is "simplest" and "smartest" is situational though.
If I have a whimsical boss who might suddenly demand that I replace a local native GUI with a web interface, then yes it could be smart for me to start with an ultra-flexible MVC implementation.
However, if I know that I won't ever need to change my front-end or back-end, then using out-of-the-box components from the Qt toolkit is the simpler and smarter option because it lets me get my app working well with a lot less code.
@JKSH the solution you propose is actually a lot more complex than it needs to be
"Replace
QLineEdit
withQDoubleSpinBox
" is much simpler than "Re-architect your whole program".BTW I did enjoy your in depth dive into this as I do enjoy having a better understanding of some of the inner workings as that in turns helps me to understand how to do things simpler and remain smart about doing it that way.
I'm glad to hear that. Live till we're old, learn till we're old (Chinese proverb)
If you'd like to do your own deep-dives, I highly recommend the Woboq Code Browser which lets us navigate the internals of Qt code interactively. I started here and traced the call chain to discover what was happening: https://code.woboq.org/qt5/qtbase/src/widgets/itemviews/qdatawidgetmapper.cpp.html#_ZN17QDataWidgetMapper7toFirstEv
-
@JKSH said in How to get comma as decimal separator in line edits?:
Given which, subclass QLineEdit is easiest.
Actually, I'd say "use QDoubleSpinBox" is easiest.
I meant, the easiest solution given wanting to continue with a
QLineEdit
-type. Just sub-classing it for (floating point) numbers is easier than writing your own delegate.So I don't get what the point of the
setItemDelegate()
is over theQDataWidgetMapper
as a whole is when you can set the widget per column?My question is here is not to do with "best solution". May I try asking again, as I did not get the answer I wanted and I really would like to understand?
I said: at the time I recommended
setItemDelegate()
I did not know aboutaddMapping()
. Given thataddMapping()
allows the specification of individual widget types per each column, I am now asking: what is the point ofsetItemDelegate()
? That is once perQDataWidgetMapper
, not per column. I don't understand when that gets used given that you have per-column-mapped-widgets. When is it used? What is the point of one delegate across all columns when you haveaddMapping()
? E.g. is it perhaps that the item delegate gets used if & only if you haven't specified anyaddMapping()
s?? Or what? I have not gathered this from reading the docs. Thank you. -
@JonB said in How to get comma as decimal separator in line edits?:
May I try asking again, as I did not get the answer I wanted and I really would like to understand?
I said: at the time I recommended
setItemDelegate()
I did not know aboutaddMapping()
. Given thataddMapping()
allows the specification of individual widget types per each column, I am now asking: what is the point ofsetItemDelegate()
? That is once perQDataWidgetMapper
, not per column. I don't understand when that gets used given that you have per-column-mapped-widgets. When is it used? What is the point of one delegate across all columns when you haveaddMapping()
? E.g. is it perhaps that the item delegate gets used if & only if you haven't specified anyaddMapping()
s?? Or what? I have not gathered this from reading the docs. Thank you.Ah, I see. Sorry for misunderstanding your question.
First, note that
QDataWidgetMapper
uses delegates a bit differently fromQAbstractItemView
.- A View asks the Delegate to create an editor widget; the Delegate can choose to create a different widget for different columns.
- A Mapper tells the Delegate which editor widget to use; the Delegate has no say here because the choice is set by
QDataWidgetMapper::addMapping()
.
In both cases though, the Delegate is responsible for transferring/converting data between the editor widget and the model.
If you don't call
QDataWidgetMapper::setItemDelegate()
, then you're using the default Delegate (which performs non-locale-aware conversion). However, if you subclassQAbstractItemDelegate
and callQDataWidgetMapper::setItemDelegate()
, then you can control how the data is converted between the editor widget and the model. For example, you can reimplementQAbstractItemModel::setEditorData()
and force it to do a locale-aware conversion before callingQLineEdit::setText()
.Does this answer your question?
-
@JKSH
So the defaultQDataWidgetMapper::itemDelegate()
has code which says "look at what column, look at theaddMapping()
s, and create theQWidget
mapped for the column", is that right? So in the normal/this case at least, you don't tend to need to do anysetItemDeletegate()
of your own, you just use the supplied one and let it work off youraddMappings()
(where e.g. you can sub-classQLineEdit
for numbers --- which to me seems the easiest without creating your own delegate), right? So if the OP uses yourNumericLineEdit
there is no need to fiddle withsetItemDelegate()
at all, you only need to in other cases? -
@JonB said in How to get comma as decimal separator in line edits?:
So the default
QDataWidgetMapper::itemDelegate()
has code which says "look at what column, look at theaddMapping()
s, and create theQWidget
mapped for the column", is that right?Nope.
With a Mapper, the Delegate does not create a widget; the Delegate is given the pointer to the widget it should read/write.
Views Mappers The editor Widget is... Created by the Delegate when the View calls QAbstractItemModel::createEditor()
Created by the programmer first, then specified via QDataWidgetMapper::addMapping()
Data is passed from the Model to the Widget when... The View calls QAbstractItemDelegate::setEditorData()
The Mapper calls QAbstractItemDelegate::setEditorData()
Data is passed from the Widget to the Model when... The View calls QAbstractItemDelegate::setModelData()
The Mapper calls QAbstractItemDelegate::setModelData()
So in the normal/this case at least, you don't tend to need to do any
setItemDeletegate()
of your own, you just use the supplied one...Yep.
...and let it work off your
addMappings()
(where e.g. you can sub-classQLineEdit
for numbers --- which to me seems the easiest without creating your own delegate), right?Yep.
So if the OP uses your
NumericLineEdit
there is no need to fiddle withsetItemDelegate()
at all, you only need to in other cases?Yep.
In summary,
QDataWidgetMapper::addMapping()
determines which widget is used.QDataWidgetMapper::setItemDelegate()
determines how data is transferred to/from the widget.
-
@JKSH
Got it! So, as I think you said,QAbstractItemDelegate
saysThe QAbstractItemDelegate class is used to display and edit data items from a model.
but you're saying
QDataWidgetMapper::itemDelegate()
does not itself do the "display and edit data items" here, that gets done via theaddMapping()
. Here we only use it for its data value transfer features. Have I finally got it? Nice and confusing.... :) -
@JonB said in How to get comma as decimal separator in line edits?:
Here we only use it for its data value transfer features. Have I finally got it?
That's a good summary :)